题目大意:n个点(从0开始),给出任意两点之间的距离,问从0到n-1的最短Hamilton路径。
Hamilton路径:从0到n-1不重不漏地经过每个点恰好一次。
题解:考虑状压dp,用dp[i][j]表示状态为i,最后处于点j的最短路径长度。答案就是dp[(1<<n)-1][n-1]。现在考虑状态如何进行转移。首先dp[1][0]=0,表示最初处于0号点。每次转移,枚举边,取min。由于从0到(1<<n)-1,前面的状态不会由后面的状态得到,故枚举状态时从0到(1<<n)-1即可。转移时需要判断是否合法:即每个点只经过一次。
时间复杂度:O(n2*2^n)。
由于存在许多非法状态,故真正的时间复杂度比O(n2*2^n)小得多。
#include<bits/stdc++.h>
using namespace std;
const int N = 21,INF = 0x3f3f3f3f;
int dp[1<<N][N];
int g[N][N];
int n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)scanf("%d",&g[i][j]);
memset(dp,0x3f,sizeof dp);
dp[1][0]=0;
for(int i=1;i<(1<<n);i++){
for(int j=0;j<n;j++){
if(dp[i][j]>=INF)continue;
for(int k=0;k<n;k++){
if(i>>k&1)continue;
dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+g[j][k]);
//cout<<(i|(1<<k))<<" "<<k<<" "<<dp[i|(1<<k)][k]<<endl;
}
}
}
printf("%d\n",dp[(1<<n)-1][n-1]);
return 0;
}