【题解】AcWing 91.最短Hamilton路径

AcWing 91.最短Hamilton路径

题目描述

给定一张 n n n 个点的带权无向图,点从 0 ∼ n − 1 0∼n−1 0n1 标号,求起点 0 0 0 到终点 n − 1 n−1 n1 的最短 Hamilton 路径。

Hamilton 路径的定义是从 0 0 0 n − 1 n−1 n1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数 n n n

接下来 n n n 行每行 n n n 个整数,其中第 i i i 行第 j j j 个整数表示点 i i i j j j 的距离(记为 a [ i , j ] a[i,j] a[i,j])。

对于任意的 x , y , z x,y,z x,y,z,数据保证 a [ x , x ] = 0 a[x,x]=0 a[x,x]=0 a [ x , y ] = a [ y , x ] a[x,y]=a[y,x] a[x,y]=a[y,x] 并且 a [ x , y ] + a [ y , z ] ≥ a [ x , z ] a[x,y]+a[y,z]≥a[x,z] a[x,y]+a[y,z]a[x,z]

输出格式

输出一个整数,表示最短 Hamilton 路径的长度。

数据范围

1 ≤ n ≤ 20 1≤n≤20 1n20
0 ≤ a [ i , j ] ≤ 1 0 7 0≤a[i,j]≤10^7 0a[i,j]107

输入样例

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例

18

题目分析

最基础的状态压缩DP问题。

先考虑最朴素的做法:枚举 0 ∼ n − 1 0∼n-1 0n1 的全排列,计算路径长度取最小值,时间复杂度 O ( n ∗ n ! ) O(n*n!) O(nn!)
而使用二进制状态压缩DP可以将其优化到 O ( n 2 ∗ 2 n ) O(n^2*2^n) O(n22n)

状态表示:我们用一个二进制数表示经过所有点的状态,若其第 i i i 位为 1 1 1,表示第 i i i 个点已经被经过。设 f [ i , j ] f[i,j] f[i,j] 表示被经过的点的状态为 i i i,且当前处于点 p p p 的所有走法种的路径最小值。

状态计算: f [ i , j ] = m i n { f [ i   x o r   ( 1 < < j ) , k ] + w [ k , j ] } f[i,j]=min\{f[i\ xor\ (1<<j),k]+w[k,j]\} f[i,j]=min{ f[i xor (1<<j),k]+w[k,j]},其中 0 ≤ k < n 0\le k<n 0k<n ( i > > j ) & 1 = 1 (i>>j)\&1=1 (i>>j)&1=1。我们先枚举二进制数 i i i,再枚举当前所处的点,接着枚举所有可以到达 i i i 状态的状态,用它们的值更新现在的值。

状态初始化: f [ 1 , 0 ] = 0 f[1,0]=0 f[1,0]=0

代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 20;
int n, m[N][N];
long long f[1 << N][N];

int main(){
    
    
    cin >> n;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            cin >> m[i][j];
    
    memset(f, 0x3f, sizeof(f));
    f[1][0] = 0;
    for (int i = 0; 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] + m[k][j]);
    
    cout << f[(1 << n) - 1][n - 1];
    return 0;
}

猜你喜欢

转载自blog.csdn.net/f4u4u4r/article/details/117967880