TSP问题是指旅行家要旅行n个城市,要求各个城市经历且仅经历一次然后回到出发城市,并要求所走的路程最短。各个城市间的距离可以用代价矩阵来表示。
假设从顶点i出发,令d(i, V')表示从顶点i出发经过V'中各个顶点一次且仅一次,最后回到出发点i的最短路径长度,开始时,V'=V-{i},于是,TSP问题的动态规划函数为:d(i,V')=min{cik+d(k,V-{k})}(k∈V')
d(k,{})=cki(k≠i)
伪代码:
for (i=1; i<n; i++) //初始化第0列 d[i][0]=c[i][0]; for (j=1; j<2n-1-1; j++) for (i=1; i<n; i++) //依次进行第i次迭代 if (子集V[j]中不包含i) 对V[j]中的每个元素k,计算[i][j]=min(c[i][k]+d[k][j-1]); 对V[2n-1-1]中的每一个元素k,计算d[0][2n-1-1]=min(c[0][k]+d[k][2n-1-2]); 输出最短路径长度d[0][2n-1-1];
笔者在写此程序时卡在了如何判断:子集V[j]中不包含i。解决方法是对n个顶点分别用0~n-1的数字编号,按个数为1,2,……,n-1的顺序生成1~n-1个元素的子集存放在数组V[2^n-1]中。这样,在判断时就可以将当前点与数组编号按位与运算(如下图所示),为0则为需要进行计算(这时才能品味按位与运算的美)。
完整代码:
#include<iostream.h> #include<math.h> int main() { int i,j,k,min,brief,n; int D[20][20]; cout<<"顶点个数:"; cin>>n; int b=(int)pow(2,n-1); for(i=0;i<n;i++) for(j=0;j<n;j++) cin>>D[i][j]; int ** Route = (int **)calloc(n, sizeof(int *)); int ** Mat = (int **)calloc(n, sizeof(int *)); for(i=0;i<n;i++) { Route[i] = (int *)calloc(b*b, sizeof(int))+i*b; Mat[i] = (int *)calloc(b*b, sizeof(int))+i*b; } for(i=0;i<b;i++) for(j=0;j<n;j++) { Route[j][i] = -1; Mat[j][i] = -1; } for(i=0;i<n;i++)//初始化第0列 Route[i][0] = D[i][0]; for(i=1;i<b-1;i++) for(j=1;j<n;j++)//依次进行第i次迭代 if( ((int)pow(2,j-1) & i) == 0)//子集V[j不包含i { min=999; for(k=1;k<n;k++) if( (int)pow(2,k-1) & i ) { brief = D[j][k] + Route[k][i-(int)pow(2,k-1)]; if(brief < min) { min = brief; Route[j][i] = min; Mat[j][i] = k;//局部最优决策 } } } min=999; for(k=1;k<n;k++) { brief = D[0][k] + Route[k][b-1 - (int)pow(2,k-1)]; if(brief < min) { min = brief; Route[0][b-1] = min;//最优解 Mat[0][b-1] = k; } } cout<<"最短路径长度:"<<Route[0][b-1]<<endl;//最短路径长度 cout<<"最短路径:"<<"1"; for(i=b-1,j=0; i>0; ) { j = Mat[j][i]; i = i - (int)pow(2,j-1); cout<<"->"<<j+1; } cout<<"->1"<<endl; for(i=0;i<n;i++) { for(j=0;j<b;j++) cout<<Route[i][j]<<" "; cout<<endl; } free(Route); free(Mat); return 0; }