Description
有N个点,有一个商人想经过所有的点恰好一次(商人最终不一定要回到起点),求商人需要走最短路程。
Input
两个整数N,M表示图的点数和边数,接下去有M行,每行三个整数a ,b ,c 表示从a到b有一条无向边,长度是c
N<=15 c<=10000
Output
输出商人需要走过的最短距离 ,如果不能到达所有的点,输出-1 。
Sample Input
4 6
1 2 1
1 4 2
4 3 4
2 3 3
2 4 6
1 3 5
Sample Output
6
将已走过的点和未走过的点用二进制编码成整数,然后再DP.
例如:共4个点,1和2已走过,3和4未走,此时状态编码为二进制0011,对应整数为3。
状态设计为c[end][state]表示为当前状态为state,且最后访问的结点为end。
状态转移为:c[end][state]=MIN(c[i][s]+dist[i][end]),i为state的二进制中是1的位置,s为将state中的第end位变0后的整数。
边界条件为:state的二进制表示中只有1位为1,其余全为0,此时表示刚出发的状态,返回0。
需要注意的是在用位运算的时候一定要记得加括号,否则因为优先级的问题会出现一些不可预知的错误,例如RE。
View Code
#include <stdio.h> #include <string.h> #define MIN(a,b) ((a)<(b)?(a):(b)) #define N 15 #define INF 10000001 int n,m; int d[N][N],c[N][1<<15]; int dp(int end,int state) { int i,j,s,ans; if(c[end][state]!=-1) return c[end][state]; if((state&(state-1))==0) return c[end][state]=0; s=state^(1<<end); ans=INF; for(i=0;i<n;i++) { if((s|(1<<i))==s) ans=MIN(ans,dp(i,s)+d[i][end]); } return c[end][state]=ans; } int main() { int i,j,ans; while(~scanf("%d%d",&n,&m)) { for(i=0;i<n;i++) { for(j=0;j<i;j++) d[i][j]=d[j][i]=INF; memset(c[i],-1,sizeof(int)*(1<<n)); } while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); a--,b--; d[a][b]=d[b][a]=MIN(d[a][b],c); } ans=INF; for(i=0;i<n;i++) ans=MIN(ans,dp(i,(1<<n)-1)); if(ans<INF) printf("%d\n",ans); else puts("-1"); } return 0; }