ACAG 0x01-4 最短Hamilton路径

ACAG 0x01-4 最短Hamilton路径

论为什么书上标程跑不过这道题……
首先,这道题与今年CSP-S2的D1T3有着异曲同工之妙,那就是——都有$O(n!)$的做法!(大雾)
这道题的正解是状压DP。
对于任意时刻,我们可以使用一个$n$位二进制数,若其第$i$位为$1$,则表示第$i$个点已经被经过,反之未被经过。因此我们可以使用$f[i][j]$表示状态为二进制数$i$,目前处于点$j$时的最短路径。
在起点时,有$f[1][0]=0$,即只经过了点$0$,而相应的状态就是只有最低位为$1$。
记得要把$f$数组的其他值均初始化为$INF$。
不难得到,我们要的最终结果是$f[(1<<n)-1][n-1]$,即经过所有点($i$的每一位都是$1$),处于终点$n-1$的最短路。
而本题的状态转移方程为:
$f[i][j]=min(f[i][j],f[i^(1<<j)][k]+dis[j][k])$ 当且仅当 (i>>j)\&1=1(i>>k)\&1=1
其中,因为$j$只能被恰好经过一次,且$k$也被恰好经过一次,所以考虑所有这样的$k$取最小值即可。

#include<bits/stdc++.h>
#define N 20

using namespace std;

int n;
int dis[N][N],f[1<<N][N];

void Read() {
	scanf("%d",&n);
	for(int i=0;i<=n-1;i++) {
		for(int j=0;j<=n-1;j++) {
			scanf("%d",&dis[i][j]);
		}
	}
	return;
}

void Solve() {
	memset(f,0x3f,sizeof(f));
	f[1][0]=0;
	for(int i=1;i<=(1<<n)-1;i++) {
		for(int j=0;j<=n-1;j++) {
			if((i>>j)&1) {
				for(int k=0;k<=n-1;k++) {
					if((i>>k)&1) {
						f[i][j]=min(f[i][j],f[i^(1<<j)][k]+dis[j][k]);
					}
				}
			}
		}
	}
	printf("%d",f[(1<<n)-1][n-1]);
	return;
}

int main()
{
	Read();
	Solve();
	return 0;
}
posted @ 2019-11-20 23:03  WalkerV  阅读(143)  评论(0编辑  收藏  举报