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
分析:对于只经过节点仅且一次的题目,很清楚用二进制dp,暴搜完所有的状态,而此题每个节点有三个状态,即没走过,走了一次,走了两次,所以用三进制表示,分别代表三种状态;用dp[i][j]表示经过节点j后到达状态i;当i状态满足所有位置没有0是,即每个点都至少经过了一次,就是一种情况,但不一定是最优情况所以要更新最小值;
程序:
#include"stdio.h" #include"string.h" #include"iostream" #include"map" #include"string" #include"queue" #include"stdlib.h" #include"algorithm" #include"math.h" #define M 60001 #define eps 1e-10 #define inf 100000000 #define mod 100000000 #define INF 0x3f3f3f3f using namespace std; int dp[M][12],px[12],a[M][12],dis[12][12],path[M][12]; void init() { int i; px[0]=1; for(i=1;i<=10;i++) px[i]=px[i-1]*3; memset(a,0,sizeof(a)); for(i=0;i<px[10];i++) { int k=i,t=0; while(k) { a[i][t++]=k%3; k/=3; } } } int main() { init(); int n,m,i,j,k; while(scanf("%d%d",&n,&m)!=-1) { memset(dis,INF,sizeof(dis)); for(i=1;i<=m;i++) { int u,v,c; scanf("%d%d%d",&u,&v,&c); u--; v--; if(dis[u][v]>c) dis[u][v]=dis[v][u]=c; } memset(dp,INF,sizeof(dp)); memset(path,-1,sizeof(path)); for(i=0;i<n;i++) { dp[px[i]][i]=0; path[px[i]][i]=-1; } int ans=INF; int I,J; for(i=1;i<px[n];i++) { int flag=1; for(j=0;j<n;j++) { if(a[i][j]==0) { flag=0;continue; } int cur=i-px[j]; for(k=0;k<n;k++) { if(dp[i][j]>dp[cur][k]+dis[k][j]) { dp[i][j]=dp[cur][k]+dis[k][j]; path[i][j]=k; } } } if(flag) { for(j=0;j<n;j++) { if(ans>dp[i][j]) { I=i; J=j; ans=dp[i][j]; } } } } /*********路径**********/ /*printf("%d->",J+1); for(k=path[I][J];k!=-1;k=path[I][k]) { printf("%d->",k+1); I=I-px[J]; J=k; } printf("\n");*/ /***********************/ if(ans<INF) printf("%d\n",ans); else printf("-1\n"); } return 0; }