《算法竞赛进阶指南》-AcWing-91. 最短Hamilton路径-题解

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法竞赛进阶指南》-AcWing-91. 最短Hamilton路径-题解相关的知识,希望对你有一定的参考价值。

最短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 不重不漏地经过每个点恰好一次。

输入样例:

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

其中 5 5 5代表一共有 5 5 5个点,下面 5 × 5 5\\times5 5×5的矩阵中 A [ i ] [ j ] A[i][j] A[i][j]代表点 i i i到点 j j j的路径。

输出样例:

18

输出就直接输出答案即可。

解题思路

  • 首先用一个数 s t a t e state state来表示当前状态,数字 s t a t e state state二进制下的第 i i i位表示点 i i i是否经过过(1表示已经经过过了)。

  • 然后用一个数组 f [ s t a t e ] [ j ] f[state][j] f[state][j]表示终点位于点 j j j且路径状态位 s t a t e state state的最短总路径。

    其中合法的 f [ s t a t e ] [ j ] f[state][j] f[state][j]必须满足 s t a t e state state的第 j j j位是 1 1 1(因为终点在 j j j表面 j j j点经过过),即 s t a t e > > j & 1 state>>j\\&1 state>>j&1为真。

  • f [ s t a t e ] [ j ] f[state][j] f[state][j]的值为所有能一步到达 j j j的点 k k k中, f [ s t a t e _ k ] [ k ] + k 到 j 的 路 径 f[state\\_k][k]+k到j的路径 f[state_k][k]+kj的最小值。

  • 初始值:起点是 0 0 0,这时候只经过了点 0 0 0,所以 s t a t e state state只有最低为是 1 1 1其他位都是 0 0 0 s t a t e state state初始值就是 1 1 1。又因为 0 0 0 0 0 0的距离是 0 0 0,所以初始值 f [ 1 ] [ 0 ] = 0 f[1][0]=0 f[1][0]=0

需要注意-的优先级大于>>

对核心代码的讲解:

这里可以先参考一下后面的完整代码

  1. memset(f, 0x3f, sizeof(f))赋初值为“无穷大”

  2.  // 输入每两点之间的距离
     for(int i=0;i<n;i++)
         for(int j=0;j<n;j++)
             cin>>A[i][j];
    
  3. f[1][0]=0初始化起点自身的状态

  4.  for(int i=0;i<1<<n;i++) // 先枚举每种状态i
         for(int j=0;j<n;j++) // 再枚举这种状态下的终点j
             if(i>>j&1) // 这种状态的第j位必须是1才有意义
                 for(int k=0;k<n;k++) // 最后枚举这种到点j的上一个点k
                     if((i-(1<<j))>>k&1) // (i-(1<<j))是上一种状态,它的第k位必须是1
                         f[i][j]=min(f[i][j], f[i-(1<<j)][k]+A[k][j]); // 取所有能到达这一点的选项中的最小值
    
  5. cout<<f[(1<<n)-1][n-1]<<endl输出最终结果:状态是起点到终点的每个点都经过,终点是n-1。


AC代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
const int N=20, M=1<<20;
int f[M][N], A[N][N];
int main()
{
    memset(f, 0x3f, sizeof(f));
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>A[i][j];
    f[1][0]=0; // 0到0的距离是0!不是1!
    for(int i=0;i<1<<n;i++) // 先枚举每种状态i
        for(int j=0;j<n;j++) // 再枚举这种状态下的终点j
            if(i>>j&1) // 这种状态的第j位必须是1才有意义
                for(int k=0;k<n;k++) // 最后枚举这种到点j的上一个点k
                    if((i-(1<<j))>>k&1) // (i-(1<<j))是上一种状态,它的第k位必须是1
                        f[i][j]=min(f[i][j], f[i-(1<<j)][k]+A[k][j]);
    cout<<f[(1<<n)-1][n-1]<<endl;
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/119218417

以上是关于《算法竞赛进阶指南》-AcWing-91. 最短Hamilton路径-题解的主要内容,如果未能解决你的问题,请参考以下文章

算法竞赛进阶指南基本算法:递推与递归

《算法竞赛进阶指南》0x03差分

算法竞赛进阶指南基础算法:前缀和与差分

《算法竞赛进阶指南》0x5C计数DP Gerald & Giant Chess

《算法竞赛进阶指南》0x25广度优先搜索 POJ3322 Bloxorz I

题解|《算法竞赛进阶指南》 Sticks