使用状压dp解决tsp问题

旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。因此,任何能使该问题的求解得以简化的方法,都将受到高度的评价和关注。
旅行推销员问题是图论中最著名的问题之一,即“已给一个n个点的完全图,每条边都有一个长度,求总长度最短的经过每个顶点正好一次的封闭回路”。Edmonds,Cook和Karp等人发现,这批难题有一个值得注意的性质,对其中一个问题存在有效算法时,每个问题都会有有效算法。 [1]
迄今为止,这类问题中没有一个找到有效算法。倾向于接受NP完全问题(NP-Complete或NPC)和NP难题(NP-Hard或NPH)不存在有效算法这一猜想,认为这类问题的大型实例不能用精确算法求解,必须寻求这类问题的有效的近似算法。
。关于TSP问题,虽然没有可以解决的有效算法,但是还是有一些复杂度差的解法可以处理数据规模小的问题,今天介绍的状压dp法结合Floyd算法就可以用2n*2^n的复杂度解决这一问题,
关于这个问题我们先设计状态,就用01串来表示每个位置有没有走过,
F[st][i]表示当前状态为st,最后到达的一个点是i,所经过的最短距离
那么就会有状态转移方程 F[st][i] = minn(f[st’][j] + a[j][i]) 其中 st’ + (1<<(j – 1)) ==st也就是说我们枚举2层状态,如果满足转移条件,即转移位一个走过一个没走过,就可以通过转移求出它的最小值,在循环结束后就可以得到途径所有点后以任意一个点结尾的所需的最短路径

下面介绍一个例题
郊区春游
题目描述
今天春天铁子的班上组织了一场春游,在铁子的城市里有n个郊区和m条无向道路,第i条道路连接郊区Ai和Bi,路费是Ci。经过铁子和顺溜的提议,他们决定去其中的R个郊区玩耍(不考虑玩耍的顺序),但是由于他们的班费紧张,所以需要找到一条旅游路线使得他们的花费最少,假设他们制定的旅游路线为V1, V2 ,V3 … VR,那么他们的总花费为从V1到V2的花费加上V2到V3的花费依次类推,注意从铁子班上到V1的花费和从VR到铁子班上的花费是不需要考虑的,因为这两段花费由学校报销而且我们也不打算告诉你铁子学校的位置。
输入描述:
第一行三个整数n, m, R(2 ≤ n ≤ 200, 1 ≤ m ≤ 5000, 2 ≤ R ≤ min(n, 15))。
第二行R个整数表示需要去玩耍的郊区编号。
以下m行每行Ai, Bi, Ci(1 ≤ Ai, Bi ≤ n, Ai ≠ Bi, Ci ≤ 10000)
保证不存在重边。
输出描述:
输出一行表示最小的花费

题目给出所有要经过的点和一些路径,我们首先需要用Floyd算法优化每两条路线之间的最短路,优化完成后,通过枚举状态和当前位数就可以完成状态转移的过程

#include
using namespace std;
int i,j,k,s,n,m,R,T[205][205],Q[20],D[1<<16][16];
int main()
{
	scanf("%d%d%d",&n,&m,&R);
	for(i=0;i<R;i++)
	scanf("%d",&Q[i]);//需要经过的点以及排序
	memset(T,0x3f3f3f3f,sizeof(T)),memset(D,0x3f3f3f3f,sizeof(D));//初始化
	for(i=1;i<=n;i++)
	T[i][i]=0;//任意点到本身的最短路都为0
	while(m--)
	scanf("%d%d%d",&i,&j,&k),T[i][j]=min(T[i][j],k),T[j][i]=T[i][j];
	for(k=1;k<=n;k++)
	for(i=1;i<=n;i++)
	for(j=1;j<=n;j++)
	if(T[i][k]+T[k][j]<T[i][j]) T[i][j]=T[i][k]+T[k][j];//Floyd算法求最短路
	s=(1<<R)-1;//枚举所有状态
	for(i=0;i<R;i++)
	D[1<<i][i]=0;
	for(i=0;i<=s;i++)
	for(j=0;j<R;j++)
	{
		if((i&(1<<j))==0) continue;//第j位如果已经走过则无需枚举
		for(k=0;k<R;k++)
		D[i^(1<<k)][k]=min(D[i^(1<<k)][k],D[i][j]+T[Q[k]][Q[j]]);
		
	}
	for(m=1e9,i=0;i<R;i++)
	m=min(m,D[s][i]);
	printf("%d\n",m);
	return 0;
}

那么如果在加一个问题, 如果要求走完所有的点回到原点怎么办?
我们可以很容易的发现,除了原点,我们可以得到其余所有在完成状态下以任意点结尾的最短路的值,只需要加上T[原点][终点]比较所有情况的大小,就可以的到这个问题的解。
对于TSP问题,这个算法的复杂度显然不够优秀,但是在出来小规模的数据范围时,还是可以满足需要的,

你可能感兴趣的:(dp)