A sample Hamilton path
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 306 Accepted Submission(s): 142
Problem Description
Give you a Graph,you have to start at the city with ID zero.
Input
The first line is n(1<=n<=21) m(0<=m<=3)
The next n line show you the graph, each line has n integers.
The jth integers means the length to city j.if the number is -1 means there is no way. If i==j the number must be -1.You can assume that the length will not larger than 10000
Next m lines,each line has two integers a,b (0<=a,b<n) means the path must visit city a first.
The input end with EOF.
The next n line show you the graph, each line has n integers.
The jth integers means the length to city j.if the number is -1 means there is no way. If i==j the number must be -1.You can assume that the length will not larger than 10000
Next m lines,each line has two integers a,b (0<=a,b<n) means the path must visit city a first.
The input end with EOF.
Output
For each test case,output the shorest length of the hamilton path.
If you could not find a path, output -1
If you could not find a path, output -1
Sample Input
3 0 -1 2 4 -1 -1 2 1 3 -1 4 3 -1 2 -1 1 2 -1 2 1 4 3 -1 1 3 2 3 -1 1 3 0 1 2 3
Sample Output
4 5HintI think that all of you know that a!=b and b!=0 =。=
题意:从编号为0的结点出发,输出一条最短的遍历所有的点(有且仅有一次)的路径,且部分点必须在某些点之前先遍历到。
做法:这属于集合dp。差不多是斯坦福的课件里面的例题了。dp[s][i]为点集合s里面以i为终点的哈密顿最优路径长度。然后再添加点拓展状态。详细看代码注释。
#include<algorithm> #include <cstring> #include<cstdio> #include<stdlib.h> #define L(i) (1 << (i)) #define MAXM 3000000 using namespace std; int dp[3000000][22]; int dis[22][22]; int f[22]; int main() { int n, m; while(~scanf("%d%d",&n, &m)) { memset(f,0,sizeof(f)); //记录每个节点之前必须经历的节点 //的集合,如:1 3 2 3 4 3.f[3]的集合是1,2,4, //利用状态压缩成一个数二进制形式为:10110 for(int i = 0;i < L(n);i ++)//dp[s][i]状态定义为在集合s里面以点i为终点 for(int j = 0;j < n;j ++)//的最优路径长度;置初值无穷大 dp[i][j] = MAXM; //这个值不能太大,否则会超内存。我分析是加到溢出非法访问数组了。。。。纯属猜测 for(int i = 0;i < n;i ++) for(int j = 0;j < n;j ++) scanf("%d",dis[i] + j); for(int i = 0;i < m;i ++) { int u, v; scanf("%d%d", &u,&v); f[v] |= L(u); //加入集合 } dp[1][0] = 0; //题目要求从ID为0的节点出发,所以只有这个才是合法的 int S = L(n); for(int s = 0;s < S;s ++) //枚举各种集合 for(int i = 0;i < n;i ++) //枚举每个集合里面的终点 if(dp[s][i] == MAXM)continue;//如果状态合法才拓展 else for(int j = 1;j < n;j ++) //枚举新加入集合中的节点 { if(dis[i][j] == -1)continue; //如果新加入点与i点没有路径则不能加入 if(!(s & L(i)))continue; //如果节点i不在集合s里面返回 if(s & L(j))continue; //如果要加入节点在集合里面返回 if(f[j] != (s & f[j]))continue;//如果新加节点的前驱节点集合 //小于当前集合,说明还有前驱点为经历, //还不能走这个点,返回 dp[s|L(j)][j] = min(dp[s][i] + dis[i][j],dp[s|L(j)][j]); } int ans = MAXM; S = L(n) - 1; for(int i = 0;i < n;i ++) //i必须从0开始,因为有一个点的情况 ans = min(ans, dp[S][i]); if(ans >= MAXM)printf("-1\n"); else printf("%d\n",ans); } return 0; }