P1171 售货员的难题
Sol:
最短Hamilton路径,经典的NPC问题,小数据可以通过状压DP 实现。
状态:\(f[i][j]\)表示当前在第i号点,且已经过的点的状态为j 时的最短Hamilton路径。
阶段:若以点为阶段,由于会从点i转移到点i+1,还可能从i+1转移到i-1,不具有无后效性,因此我们考虑以二进制状态为阶段进行转移。
决策:考虑由哪一个点转移而来。
转移:\(f[i][j]=\max \limits_{i\&(1<<i)\&\&i\&(1<<k)}(f[i][j],f[i\&(~(1<<k))][k]+dis[k][j])\)
注意:
1.判断好DP的状态,如果发现DP瞎转移or不转移时,多半是阶段找错了。
AC Code:
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
const int N = 20;
int d[N][N];
int f[1<<N][N];
int read(){
int x=0,f=1;char ch=' ';
while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int main(){
// freopen("data.in","r",stdin);
// freopen("sol.out","w",stdout);
double st=clock();
int n;n=read();
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
d[i][j]=read();
memset(f,0x3f,sizeof(f));
f[1][0]=0;
for(int i=1;i<(1<<n);i++){
for(int j=0;j<n;j++) if((i>>j)&1){
for(int k=0;k<n;k++) if((j!=k)&&((i>>k)&1)){
if(f[i][j]>f[i^(1<<j)][k]+d[k][j]){
f[i][j]=f[i^(1<<j)][k]+d[k][j];
}
}
}
}
int ans=(1<<30);
for(int i=0;i<n;i++){
ans=min(ans,f[(1<<n)-1][i]+d[i][0]);
}
printf("%d",ans);
return 0;
}