我正在参与掘金创作者训练营第5期
@TOC
最短Hamilton路径
题目描述
给定一张 个点的带权无向图,点从 标号,求起点 到终点 的最短 Hamilton 路径。
Hamilton 路径的定义是从 到 不重不漏地经过每个点恰好一次。
输入样例:
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
输出就直接输出答案即可。
解题思路
首先用一个数来表示当前状态,数字二进制下的第位表示点是否经过过(1表示已经经过过了)。
然后用一个数组表示终点位于点且路径状态位的最短总路径。
其中合法的必须满足的第位是(因为终点在表面点经过过),即为真。
的值为所有能一步到达的点中,的最小值。
初始值:起点是,这时候只经过了点,所以只有最低为是其他位都是,初始值就是。又因为到的距离是,所以初始值。
需要注意-
的优先级大于>>
对核心代码的讲解:
这里可以先参考一下后面的完整代码
-
memset(f, 0x3f, sizeof(f))
赋初值为“无穷大” -
// 输入每两点之间的距离 for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>A[i][j];
-
f[1][0]=0
初始化起点自身的状态 -
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
输出最终结果:状态是起点到终点的每个点都经过,终点是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;
}
同步发文于CSDN,原创不易,转载请附上原文链接哦~
Tisfy:letmefly.blog.csdn.net/article/det…