题目让求最小值,这种只求最小(大)值而不要方案的一般都跟动态规划有关。所以这题也可考虑用DP解。
题目要求有n-1个点时的最短Hamilton路径,而如果我们能先求出有n-2号点时的最短路径,就只需找出这n-2个点中那个点到n-1号点最近就行了。这就是本题的一个子问题,显然有n-1个点时的最优解可以由有n-2个点时的最优解导出,这就有了最优子结构。而且当我们考虑从前n-2个点中的哪个点走到n-1号点时,无论决策是什么,都不会对只有n-2个点时的最优解产生影响,所以就具有了后无效性。综上,我们可以通过动态规划解出这道题。
如何描述状态呢?因为不要求按字典序过点,所以我们不仅要描述现在在哪个点,还要知道已经过了哪些点。要知道现在在哪很容易,但过了哪些点就不太好搞。可以这样考虑,因为每个点要么过了,要么没过,如果用1表示过了,用0表示没过,那么就可以用一串10来表示各个点的状态,并且可以用这串10在二进制下表示的十进制数来储存状态,这样就能用一个二维数组来表示完整的状态了。其实也就是状态压缩DP。
我们用f[i][j]表示当前在j号点,其他点状态以i的二进制形式表示,从低位到高位一次表示0号到n-1号。
转移方法就是枚举一下i中已经为1的点,选离j号点最近的点就行了。用k表示i中为1的点,得到的转移方程为:
f[i][j]=min(f[i][j],f[i^(1<<j)][k]+a[j][k])
参考代码如下
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int a[25][25],f[1<<20][25]; int main() { int n; scanf("%d",&n); for(int i=0;i<n;++i) for(int j=0;j<n;++j) scanf("%d",&a[i][j]); //f[i][j]=min(f[i^(1<<j)][k]+a[j][k]) (i^(1<<j)>>k)&1==1 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(((i^(1<<j))>>k)&1) f[i][j]=min(f[i][j],f[i^(1<<j)][k]+a[j][k]); printf("%d\n",f[(1<<n)-1][n-1]); return 0; }