來源:http://acm.hdu.edu.cn/showproblem.php?pid=3001
題意:n個點,m個邊,求遍曆所有邊的最短路徑,並且每點最多訪問兩次。
分析:很經典的TSP問題,但是題目又有改變,每個旅遊地可以走兩遍,故進行擴充2狀態的狀態壓縮DP伸展為3狀態的狀態壓縮問題。0表示沒去過此點,1 表示去過一次,2表示去過兩次,狀態轉移方程基本和以前一樣dp[i][j]=min(dp[i][k]+map[k][j])。
#include<iostream>#include<algorithm>#include<iomanip>#include<cstring>#include<string>#include<cstdio>#include<cmath>#include<queue>#include<set>#define inf 0x1f1f1f1f#define min(a,b) (a) < (b) ? (a) : (b)using namespace std;int n,m;int tri[12] = {0,1,3,9,27,81,243,729,2187,6561,19683,59049};int digit[59050][11];int map[11][11],dp[59050][11];int main(){ for(int i=0;i<59050;i++) { int t=i; for(int j=1;j<=10;j++) { digit[i][j]=t%3; t/=3; if(t==0) break; } } while(cin>>n>>m) { memset(map,inf,sizeof(map)); int a,b,c; while(m--) { scanf("%d%d%d",&a,&b,&c); if(c<map[a][b]) map[a][b]=map[b][a]=c; } memset(dp,inf,sizeof(dp)); for(int i=1;i<=n;i++) dp[tri[i]][i]=0; int ans=inf; for(int s=0;s<tri[n+1];s++) { int flag=1; for(int i=1;i<=n;i++) { if(digit[s][i]==0) flag = 0; if(dp[s][i]==inf) continue; for(int j=1;j<=n;j++) { if(i==j) continue; if(map[i][j]==inf||digit[s][j]>= 2) continue; int news=s+tri[j]; dp[news][j]=min(dp[news][j],dp[s][i]+map[i][j]); } } if(flag) { for(int j=1;j<=n;j++) ans=min(ans,dp[s][j]); } } if(ans==inf) { puts("-1"); continue; } printf("%d\n",ans); } return 0;}