Description
给定一张 n n n 个点的带权完全无向图,求出从 1 1 1 到 n n n 经过每个点恰好一次的最短路。
Analysis
由于 n ≤ 20 n \le 20 n≤20,可以选用 状压dp 来解决问题。
设
d
p
S
,
v
dp_{S,v}
dpS,v 表示经过的点集合为
S
S
S,且当前所在的点为
u
u
u 时的路径长度。
状态转移方程显然为
d
p
S
,
v
=
min
u
∈
S
{
d
p
S
−
{
v
}
,
u
+
w
(
u
,
v
)
}
dp_{S,v}=\min\limits_{u \in S} \{ dp_{S-\{v\},u} + w(u,v) \}
dpS,v=u∈Smin{dpS−{v},u+w(u,v)}
接下来按照 状压dp 的套路,外层枚举 S S S,第二层枚举 v v v(注意判断是否和 S S S 矛盾),第三层枚举 u ∈ S u \in S u∈S 进行转移。
这里可以进行一个常数优化,枚举 S S S 时,只需要枚举包含 1 1 1 号点的即可(另外 S = { } S=\{\} S={} 和 S = { 1 } S=\{1\} S={1} 显然也无用)。
Code
// Problem: P10447 最短 Hamilton 路径
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P10447
// Memory Limit: 512 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int INF = 1e9;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
int up = 1 << n;
vector dis(n, vector<int>(n));
vector dp(up, vector<int>(n, INF));
for (auto &x : dis)
for (auto &y : x) cin >> y;
dp[1][0] = 0;
for (int S = 3; S < up; S += 2)
for (int v = 1; v < n; v++) if ((S >> v) & 1)
for (int u = 0; u < n; u++)
if ((S >> u) & 1) chmin(dp[S][v], dp[S - (1 << v)][u] + dis[u][v]);
cout << dp[up - 1][n - 1] << endl;
return 0;
}