目录
最短Hamilton路径
题目描述
给定一张 n n n 个点的带权无向图,点从 0 ∼ n − 1 0∼n−1 0∼n−1 标号,求起点 0 0 0 到终点 n − 1 n−1 n−1 的最短 Hamilton 路径。
Hamilton 路径的定义是从 0 0 0 到 n − 1 n−1 n−1 不重不漏地经过每个点恰好一次。
输入样例:
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]+k到j的路径的最小值。
初始值:起点是 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。
需要注意-
的优先级大于>>
对核心代码的讲解:
这里可以先参考一下后面的完整代码
-
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;
}
原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/119218417