题目让求最小值,这种只求最小(大)值而不要方案的一般都跟动态规划有关。所以这题也可考虑用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;
}