Description
给定一张 n n n 点 m m m 边的有向图(可能有环),点的下标从 0 0 0 开始,求 0 0 0 到 n − 1 n-1 n−1 的最长路(不允许重复走点和边)。 2 ≤ n ≤ 18 2 \le n \le 18 2≤n≤18。
Analysis
由于图不保证无环,所以不能用 Dijkstra
或 SPFA
来求最长路。
由于
n
n
n 很小,考虑状压dp。设
d
p
S
,
u
dp_{S,u}
dpS,u 表示当前走到
u
u
u,经过的点集合为
S
S
S 的最长路。
易得状态转移方程为:
d
p
S
,
u
=
max
d
p
S
∪
{
v
}
,
v
+
w
(
u
,
v
)
(
<
u
,
v
>
∈
E
,
v
∉
S
)
dp_{S,u}=\max dp_{S \cup \{v\},v} + w(u,v)\qquad\qquad(<u,v> \in E,v \notin S)
dpS,u=maxdpS∪{v},v+w(u,v)(<u,v>∈E,v∈/S)
可以用 记忆化搜索 去实现。
Code
// Problem: P4802 [CCO2015] 路短最
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4802
// Memory Limit: 250 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<PII>> G(n);
for(int i = 0, u, v, w; i < m; i++){
cin >> u >> v >> w;
G[u].push_back({v, w});
}
vector<vector<int>> dp(1 << n, vector<int>(n, 0));
auto dfs = [&](auto self, int u, int state) -> int{
if(u == n - 1) return 0;
if(dp[state][u]) return dp[state][u];
int len = -INF;
for(auto &edge: G[u]){
int v = edge.first, w = edge.second;
if(!(state & (1 << v))) len = max(len, self(self, v, state | (1 << v)) + w);
}
return dp[state][u] = len;
};
cout << dfs(dfs, 0, 1) << endl;
return 0;
}