• AcWing91.最短 Hamilton 路径


    题目

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

    Hamilton 路径的定义是从 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 , y ] = a [ y , x ] a[x,x]=0,a[x,y]=a[y,x] a[x,x]=0a[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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出样例

    18
    
    • 1

    分析

    很容易想到本题的一种 “朴素” 做法,就是枚举 n n n 个点的全排列,计算路径长度取最小值,时间复杂度为 O ( n ∗ n ! ) O(n * n!) O(nn!),使用下面的二进制状态压缩 DP 可以优化到 O ( n 2 ∗ 2 n ) O(n^2 * 2^n) O(n22n)

    在任意时刻如何表示哪些点已经被经过,哪些点没有被经过?可以使用一个 n n n 位二进制数,若其第 i i i 位( 0 ≤ i < n 0 \le i \lt n 0i<n) 为 1,则表示第 i i i 个点已经被经过,反之未被经过。

    在任意时刻还需要知道当前所处的位置,因此可以使用 F [ i , j ] ( 0 ≤ i < 2 n , 0 ≤ j < n ) F[i,j](0 \le i \lt 2^n, 0 \le j \lt n) F[i,j]0i<2n,0j<n 表示 “点被经过的状态” 对应的二进制数为 i i i,且目前处于点 j j j 时的最短路径。

    在起点时,有 F [ 1 , 0 ] = 0 F[1,0] = 0 F[1,0]=0,即只经过了点 0( i i i 只有第 0 位为 1),目前处于起点 0,最短路径长度为 0。为了方便起见, F F F 数组其他的值设为无穷大目标是 F[(1 << N) - 1, n - 1],即经过所有点 ( i i i 的所有位都是 1),处于终点 n − 1 n - 1 n1 的最短路。

    在任意时刻,有公式 F[i,j] = min{F[i ^ (1 << j), k] + weight(k,j)},其中 0 ≤ k < n 0 \le k \lt n 0k<n 并且 ((i >> j) & 1) = 1,即当前时刻 “被经过的点的状态” 对应的二进制数为 i i i,处于点 j j j。因为 j j j 只能被恰好经过一次,所以一定是刚刚经过的,故在上一时刻 “被经过的点的状态” 对应的二进制数的第 j j j 位应该赋值为 0,也就是 i ^ (1 << j)。另外,上一时刻所处的位置可能是i ^ (1 << j) 中任意一个是 1 的数位 k k k,从 k k k 走到 j j j 需经过 weight(k,j) 路程,可以考虑所有这样的 k k k 取最小值。这就是以上公式的由来。

    代码

    #include 
    #include 
    using namespace std;
    
    const int N = 1 << 20;
    int F[N][20]; //F[i][j]表示被经过的点的状态为i,目前处于点j的最短路径
    int weight[20][20];
    
    int hamilton(int n) {
        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) { //第j位为1
                    for (int k = 0; k < n; k++) {
                        if ((i ^ (1 << j)) >> k & 1) { //当前在k点,要去j点
                            F[i][j] = min(F[i][j], F[i ^ (1 << j)][k] + weight[k][j]);
                        }
                    }
                }
            }
        }
        
        return F[(1 << n) - 1][n - 1];
    }
    
    int main() {
        int n;
        cin >> n;
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> weight[i][j];
            }
        }
        
        cout << hamilton(n) << endl;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    JavaWeb_第5章_JSP
    EFCore的使用笔记
    如何确认linux的包管理器是yum还是apt,确认之后安装其他程序的时候就需要注意安装命令
    html静态网站基于品优购电商购物网站网页设计与实现共计3个页面 html+css+javascript网页设计实例 企业网站制作
    基于SVM+Webdriver的智能NBA常规赛与季后赛结果预测系统——机器学习算法应用(含python、ipynb工程源码)+所有数据集(四)
    Linux IO模式以及select、poll、epoll详解
    【PostgreSQL的shared_buffers和系统OS cache的关系】
    正则表达式(Java)
    YOLOv5实现佩戴安全帽检测和识别(含佩戴安全帽数据集+训练代码)
    typescript学习(二)
  • 原文地址:https://blog.csdn.net/u011386173/article/details/134543374