http://acm.hdu.edu.cn/showproblem.php?pid=3001
2 1 1 2 100 3 2 1 2 40 2 3 50 3 3 1 2 3 1 3 4 2 3 10
100 90 7
/** hdu3001 状态压缩dp+三进制。 每个顶点经过最多2次,也就是说有0,1,2三总状态,我们状态转移的时候要用三进制。另外起点任意, 所以dp[bit[i]][i]=0,剩下的初始化为INF。状态转移方程为:dp[next][l] = min(dp[next][l], dp[i][j] + load[j][l]); 其中( next = i + bit[l])。 三进制没有对应的移位操作因此我们要模拟实现:首先把0~3^10之间的所有数都用一个数组num[i][j]表示出来, 1~10位分别代表i化为三进制后每个位上对应的值(第一位对应个位,第10位对应最高位),然后状态转移就可以了 */ #include<stdio.h> #include<string.h> #include<iostream> #include <algorithm> #define INF 0x3f3f3f3f using namespace std; int n, m, minn; int load[12][12]; int bit[11] = {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049}; int dp[60000][12]; int num[60000][12];//记录每个数的三进制数 void make_trb()//计算所有数的三进制表示 { for(int i = 0; i < bit[10]; i++) { int b = i; for(int j = 0; j < 10; j++) { num[i][j] = b % 3; b /= 3; } } } int main() { make_trb();//计算所有数的三进制表示 while(~scanf("%d%d", &n, &m)) { memset(load, -1, sizeof(load)); for(int i = 0; i < m; i++) { int a,b,c; scanf("%d%d%d", &a, &b, &c); if(load[a-1][b-1] == -1)//去重边 load[b-1][a-1] = load[a-1][b-1] = c; else load[b-1][a-1] = load[a-1][b-1] = min(load[a-1][b-1], c); } memset(dp,0x3f3f3f3f,sizeof(dp)); for(int j = 0; j < n; j++) dp[ bit[j] ][j] = 0;//对每个点定为初始点0 int flag,next; minn = INF; for(int i = 0; i < bit[n]; i++) { flag = 1;//表示所有位都为1,即所有的城市都遍历过1次以上 for(int j = 0; j < n; j++)//跟二进制一样遍历所有终点 { if(num[i][j] == 0) flag = 0; if(dp[i][j] == INF) continue; for(int l = 0; l < n; l++) { if(j == l || num[i][l] >= 2 || load[l][j] == -1) continue; next = i + bit[l]; dp[next][l] = min(dp[next][l], dp[i][j] + load[j][l]); }//由于跟访问路径有关,所以for l 0 -> n,用来历遍所有起点 } if(flag == 1) { for(int j = 0; j < n; j++) minn = min(minn , dp[i][j]); } } if(minn == INF) minn = -1; printf("%d\n", minn); } return 0; }