题意
- 从0出发经过所有的点(可以重复)再回到0的最短时间
题解
- dp[i][j]表示在i状态到达j的最短时间。最后答案是min(dp[1<<(n-1)][1…n] + mp[1…n][0])
- floyd求出任意两点之间的最短时间。
- 状态转移:找一个中转站k,再到目的地j,其中状态i包含j和k。dp[i][j] = min(dp[i][j],dp[i^(1<<(j-1))][k] + mp[k][j])。如果没有中转站,那么直接到达j,dp[i][j] = mp[0][j]。i^(1<<(j-1))表示没有经过j的状态。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int const N = 10 + 5;
int const inf = 0x7f7f7f7f;
int n,ans;
int mp[N][N];
int dp[1<<N][N]; //dp[i][j]表示i状态经过j的最短时间
int main(){
while(~scanf("%d",&n) && n){
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
scanf("%d",&mp[i][j]);
for(int k=0;k<=n;k++)
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
if(mp[i][k] < inf && mp[k][j] < inf && mp[i][j] > mp[i][k] + mp[k][j])
mp[i][j] = mp[i][k] + mp[k][j];
memset(dp,inf,sizeof(dp));
for(int i=0;i<(1<<n);i++){ //枚举状态
for(int j=1;j<=n;j++){ //求到j的最短时间
if(i & (1<<(j-1)) == 0) continue; //没有经过点j
if(i == (1<<(j-1))) dp[i][j] = mp[0][j]; //如果只经过点j,那么最短距离为从0直接到j
else{
for(int k=1;k<=n;k++){
if(j != k && i&(1<<(k-1))) //如果没有经过点k
dp[i][j] = min(dp[i][j],dp[i^(1<<(j-1))][k] + mp[k][j]);
}
}
}
}
ans = inf;
for(int i=1;i<=n;i++)
ans = min(ans,dp[(1<<n)-1][i] + mp[i][0]);
printf("%d\n",ans);
}
return 0;
}