Description
给定一张 nnn 点 mmm 边的有向图(可能有环),点的下标从 000 开始,求 000 到 n−1n-1n−1 的最长路(不允许重复走点和边)。2≤n≤182 \le n \le 182≤n≤18。
Analysis
由于图不保证无环,所以不能用 Dijkstra 或 SPFA 来求最长路。
由于 nnn 很小,考虑状压dp。设 dpS,udp_{S,u}dpS,u 表示当前走到 uuu,经过的点集合为 SSS 的最长路。
易得状态转移方程为:
dpS,u=maxdpS∪{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;
}
456

被折叠的 条评论
为什么被折叠?



