• 【题解】Acwing 91 最短Hamilton路径


    最短Hamilton路径

    题目传送门

    给定一张 n 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。
    Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
    

    输入格式

    第一行输入整数n。
    
    接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(记为a[i,j])。
    
    对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。
    

    输出格式

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

    数据范围

    1≤n≤20
    0≤a[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
    

    思路分析:

    这道题很容易想到的朴素算法,枚举n个点的全排列。

    时间复杂度为O((n * n!))

    使用下面的状态压缩dp可以优化到O((n^2 * 2 ^ n))

    比如在 0, 1, 2, 3 这几个点中,我们需要从0 -> 3

    0 -> 2 -> 1 -> 3 所需10

    0 -> 1 -> 2 -> 3 所需20

    我们肯定只选第一条路

    很容易理解,若还有其他的点,第二条路总是会比第一条花费多,

    所以第二条边没有维护的必要,可以给它删除,保留第一条这条更优的。

    注意,删除的前提是他们二者访问的点的个数是一样的,现在在的点的位置是一样的。

    但朴素的爆搜还是会搜下去,直到搜完全部点。

    这就是主要优化的地方。

    (可以去看一下 Acwing y总的视频讲解,本人虽然知道 状压dp 的时间复杂度比 爆搜 要低

    但一直搞不清楚它这个 状压dp 优化在哪,看了y总的讲解恍然大悟)

    最大有20个点,为了表示每个点的状态当前点的位置

    可以开一个f[1 << 20][20]来记录 在 i 状态 j 位置的最小代价

    i 状态 j 位置 的代价 需要从 i 状态 下访问过不为j 的k点来到达

    只需要f[i][j] = min(f[i][j], f[i - (1<<j)][k])

    下方代码有本人认为根据本题要求可以做出的一些小优化?注释标注。

    代码展示:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    
    const int N = 25;
    int n, g[N][N], f[1 << 20][20]; // i 状态 j位置
    
    inline void work (int n) {
        memset(f, 0x3f, sizeof f);
        f[1][0] = 0;
        for(int i = 1; i < 1 << n; i += 2)  // 修改  i ++ -> i += 2。遍历状态
            for(int j = 1; j < n; j ++) if((i >> j) & 1) // j = 0 -> 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] + g[k][j]);
        printf("%d", f[(1 << n) -1][n - 1]);
    }
    
    int main()
    {
        scanf("%d", &n);
        for(int i = 0; i < n; i ++)
            for(int j = 0; j < n; j ++)
                scanf("%d", &g[i][j]);
        work(n);
        return 0;
    }
    
  • 相关阅读:
    PrintWriter write与println方法的区别
    java线程之一 单线程
    Android系列之ListView实现分页和类似异步加载效果(转载)
    Failed to fetch URL http://dlssl.google.com/android/repository/addons_list
    java rpc 综述(上)
    横竖屏切换时候Activity的生命周期
    【转载】C# 大数相乘
    ASP.NET 2.0 中使用PreviousPage的强类型属性
    人生一世
    SQL 中的indexof函数CHARINDEX
  • 原文地址:https://www.cnblogs.com/RemnantDreammm/p/14126682.html
Copyright © 2020-2023  润新知