开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情
最短Hamilton路径
给定一张 个点的带权无向图,点从 标号,求起点 到终点 的最短 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
输出样例:
1
题目分析
这是一道状态压缩的题目,自我感觉具体方案和下节的flyod很相似。
我们假设起点为 sta,终点为 fin,中间路径中一点为 mid。
从动态规划的角度考虑,我们可以知道从 fin 到 mid 的所有路径中,我们不需要知道具体的路径以及最优路径,只需要记录最优路径对应的消耗。
定义 表示在状态 以及当前终点为 的情况下,路径消耗最小值。
此处的状态为一个二进制串,如 : 100101,表示在经过了点 0,2,5 的路径中,到达 j 的最小路径( j 为路径中三个点中的一个)。
欲得到 的值,需要对 中路径所有到达点作为 fin 的前一点 mid,以 转移。
注意代码中的细节实现,本文进行了位运算的优化,最终时间复杂度为
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 21;
int n;
int g[N][N];
int f[1 << N][N];
int log_2[1 << N];
int main()
{
cin >> n;
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++)
cin >> g[i][j];
for (int i = 0; i < n; i ++) log_2[1 << i] = i;
memset(f, 0x3f, sizeof f);
f[1][0] = 0;
for (int i = 1; i < 1 << n; i ++)
if (i & 1)
{
for (int st = i; st; st &= st - 1)
{
int fin = st & (-st);
for (int ts = i ^ fin; ts; ts &= ts - 1)
{
int sta = ts & (-ts);
int ls = log_2[sta], lf = log_2[fin];
f[i][lf] = min(f[i][lf], f[i ^ fin][ls] + g[ls][lf]);
}
}
}
cout << f[(1 << n) - 1][n - 1];
return 0;
}