状态压缩DP:旅行商问题

题目描述:给定一个n个顶点组成的带权有向图的距离矩阵d( i , j ) ( INF表示没有边 )。要求从顶点0出发,经过每个顶点恰好一次后再回到顶点0.问所经过的边的总权重的最小值是多少?

限制条件:

  • 2 <= n <= 15
  • 0 <= d( i , j ) <= 1000

题目解析:

这个问题就是著名的旅行商问题(TSP)。所有可能的路线有(n-1)!种。这是一个非常大的值,即使题中n已经很小了,仍然无法试遍每一种情况。对于这个问题,我们可以使用DP来解决,首先写出它的递推公式。

假设现在已经访问过的顶点集合(起点0当作还未访问过的顶点)为S,当前所在的顶点为v,用dp[S][v]表示从v出发访问剩余的所有顶点,最终回到顶点0的路径的权重总和最小值。由于从v出发可以移动到任意一个节点u不属于S。因此有如下递推公式:

  • dp[V][0] = 0
  • dp[S][v] = min{ dp[ SU{u} ][u] + d(v,u) | u不属于S}

由于在这个递推公式中,有一个下标是集合而不是普通的整数,因此需要稍加处理。我们可以把它编码成一个整数,或者给它定义一个全序关系并用二叉搜索树存储,从而可以使用记忆话搜索来求解,特别的,对于集合我们可以把没一个元素的选取与否对应到一个二进制位里,从而把状态压缩成一个整数,大大方便了计算与维护。

代码实例:

int n;
int d[MAX_N][MAX_N];

int dp[1 << MAX_N][MAX_N];

int rec(int S,int v){
	if(dp[S][v] >= 0)
		return dp[S][v];
	if(S == (1 << n) - 1 && v == 0)
		return dp[S][v] = 0;//已经访问过所有节点并回到0节点 
	int res = INF;
	for(int u = 0;u < n;u++){
		if(!(S >> u & 1))//u不在集合S中 
		//下一步移动到顶点u 
			res = min(res,rec(s | 1 << u,u) + d[v][u]);
	}
	return dp[S][v] = res;
}
原文地址:https://www.cnblogs.com/long98/p/10352221.html