链接:http://poj.org/problem?id=3311
题意:有N个地点和一个出发点(N<=10),给出全部地点两两之间的距离,问从出发点出发,走遍全部地点再回到出发点的最短距离是多少。
思路:首先用floyd找到全部点之间的最短路。然后用状态压缩,dp数组一定是二维的,假设是一维的话不能保证dp[i]->dp[j]一定是最短的。由于dp[i]记录的“当前位置”不一定是能使dp[j]最小的当前位置。所以dp[i][j]中,i表示的二进制下的当前已经经过的状态,j表示的是在当前状态下眼下所在的位置。
代码:
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <ctype.h> #include <iostream> #include <map> #include <queue> #include <set> #include <stack> #include <string> #include <vector> #define eps 1e-8 #define INF 0x7fffffff #define PI acos(-1.0) #define seed 31//131,1313 #define maxn 15 typedef long long LL; typedef unsigned long long ULL; using namespace std; int dp[1<<10][maxn]; int Pow[maxn]; int back[maxn]; int cost[maxn][maxn]; void init1() { Pow[0]=1; for(int i=1; i<=10; i++) Pow[i]=Pow[i-1]*2; } void init2() { for(int i=1; i<(1<<10); i++) for(int j=0; j<=10; j++) dp[i][j]=INF; } int floyd(int a[][maxn],int t) { for(int i=0; i<t; i++) for(int j=0; j<t; j++) for(int k=0; k<t; k++) a[i][j]=min(a[i][k]+a[k][j],a[i][j]); } int main() { int T,x; init1(); while(scanf("%d",&T)) { init2(); if(!T) break; for(int i=0; i<T+1; i++) for(int j=0; j<T+1; j++) scanf("%d",&cost[i][j]); floyd(cost,T+1); for(int i=0; i<T; i++) dp[Pow[i]][i]=cost[0][i+1]; for(int i=0; i<T; i++) back[i]=cost[i+1][0]; for(int i=0; i<T; i++) for(int j=0; j<T; j++) cost[i][j]=cost[i+1][j+1]; for(int i=0; i<(1<<T); i++) { if(i==1||i==2||i==4||i==8||i==16||i==32||i==64||i==128||i==256||i==512) continue; int ii=i; int pos=0; while(ii) { if(ii%2==1) { int t=i-Pow[pos]; for(int j=0; j<T; j++) if(dp[t][j]!=INF&&dp[t][j]+cost[j][pos]<dp[i][pos]) dp[i][pos]=dp[t][j]+cost[j][pos]; } ii>>=1; pos++; } } int ans=INF; for(int i=0; i<T; i++) { if(dp[(1<<T)-1][i]!=INF&&dp[(1<<T)-1][i]+back[i]<ans) ans=dp[(1<<T)-1][i]+back[i]; } printf("%d ",ans); } return 0; }