/* 如果对于任意数目的n个城市,分别用1~n编 号,则这个问题归结为在有向带权图中,寻找一 条路径最短的哈密尔顿回路问题。 这里,V表示城市顶点,(i,j) ∈E 表示城市之 间的距离,用邻接矩阵C表示城市之间的距离。 思想: 1设d(i,V-{i})表示从顶点i出发,经过V-{i}中各顶点一次,回到顶点i的最短路径长度 2d(i,V-{i}) = d(i,|V) = k属于V min{Cik,d(i,!V - {k})} 3边界:d(k,空集) = Cki,k!= i,从顶点k出发,不经过任何顶点,回到顶点i的长度,自然是Cki 4目标状态:d(0,{1,2,3}) 难点:如何标识这个集合V,难道用标记法,这是集合上的动态规划问题,可以通过位来进行操作 0001表示第i个节点选中 那初始时:1111...1110 n-1个1 1个0 输入说明:0标识不可达 输入: 4 0 3 6 7 5 0 2 3 6 4 0 2 3 7 5 0 4 0 8 5 6 6 0 8 5 7 9 0 5 9 7 8 0 输出: 10 23 难点: 1到底递归求的是什么? 分析即所得,求的是集合 求的是:d(i,V-{i}) = d(iStart,iFull - (1<<iStart) ) 2递归传入的参数是什么? iSart,初始节点,除去初始节点的剩余节点集合,为一个数 3递归基是什么? 递归基是:如果碰到已经求过的,则直接返回;碰到i != g_iStart && s == 0,返回g_dp[i][s] = g_iArr[i][g_iStart]; 4如何挑选k? for(int k = 0 ; k < n ; k++) { //如果已经含有k,才挑选 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k属于V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); */ /* 关键: 1 //对于多个状态,只能用递归来做。 2 //记忆化搜索 if(g_dp[i][s] != -1) { return g_dp[i][s]; } //递归基 if(i != g_iStart && s == 0) { return g_dp[i][s] = g_iArr[i][g_iStart]; } 3 for(int k = 0 ; k < n ; k++) { //如果已经含有k,才挑选 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k属于V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); //选取最小值 if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k]) { iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k];i } 4 1设d(i,V-{i})表示从顶点i出发,经过V-{i}中各顶点一次,回到顶点i的最短路径长度 2d(i,V-{i}) = d(i,|V) = k属于V min{Cik,d(i,!V - {k})} 3边界:d(k,空集) = Cki,k!= i,从顶点k出发,不经过任何顶点,回到顶点i的长度,自然是Cki 4目标状态:d(0,{1,2,3}) */ #include <stdio.h> #include <string.h> const int MAXSIZE = 20; int g_dp[MAXSIZE][1 << MAXSIZE];//采用集合的方式来做 int g_iArr[MAXSIZE][MAXSIZE]; int g_iStart = 0; //对于多个状态,只能用递归来做。 int TSP(int i,int s,int n) { //记忆化搜索 if(g_dp[i][s] != -1) { return g_dp[i][s]; } //递归基 if(i != g_iStart && s == 0) { return g_dp[i][s] = g_iArr[i][g_iStart]; } //开始进行递推,从底向上 int iMin = 1000000000; //选择下一个城市 for(int k = 0 ; k < n ; k++) { //如果已经含有k,才挑选 if(s & (1 << k)) { //d(i,V-{i}) = d(i,|V) = k属于V min{Cik,d(i,!V - {k})} g_dp[k][s ^ (1<<k)] = TSP(k,s ^ (1<<k),n); //选取最小值 if(iMin > g_dp[k][s ^ (1<<k)] + g_iArr[i][k]) { iMin = g_dp[k][s ^ (1<<k)] + g_iArr[i][k]; } } } return g_dp[i][s] = iMin; } void process() { int n; while(EOF != scanf("%d",&n)) { if(n <= 0) { break; } memset(g_iArr,0,sizeof(g_iArr)); for(int i = 0 ; i <= n - 1 ; i++) { for(int j = 0 ; j <= n - 1 ; j++) { scanf("%d",&g_iArr[i][j]); } } int iStart = 0; memset(g_dp,-1,sizeof(g_dp)); //假设选定i=1作为初始节点 //初始化动态规划数组 for(int i = 0 ; i <= n-1 ; i++) { if(i != iStart) { g_dp[i][0] = g_iArr[i][iStart]; } } TSP(iStart,(1<<n)-2,n); printf("%d\n",g_dp[iStart][(1<<n)-2] ); } } int main(int argc,char* argv[]) { process(); getchar(); return 0; }