F - Negative Traveling Salesman
F - Negative Traveling Salesman
Problem Statement
There is a weighted simple directed graph with vertices and edges. The vertices are numbered to , and the -th edge has a weight of and extends from vertex to vertex . The weights can be negative, but the graph does not contain negative cycles.
Determine whether there is a walk that visits each vertex at least once. If such a walk exists, find the minimum total weight of the edges traversed. If the same edge is traversed multiple times, the weight of that edge is added for each traversal.
Here, "a walk that visits each vertex at least once" is a sequence of vertices that satisfies both of the following conditions:
- For every , there is an edge extending from vertex to vertex .
- For every , there is such that .
Constraints
- for
- The given graph does not contain negative cycles.
- All input values are integers.
Input
The input is given from Standard Input in the following format:
Output
If there is a walk that visits each vertex at least once, print the minimum total weight of the edges traversed. Otherwise, print No
.
Sample Input 1
3 4
1 2 5
2 1 -3
2 3 -4
3 1 100
Sample Output 1
-2
By following the vertices in the order , you can visit all vertices at least once, and the total weight of the edges traversed is . This is the minimum.
Sample Input 2
3 2
1 2 0
2 1 0
Sample Output 2
No
There is no walk that visits all vertices at least once.
Sample Input 3
5 9
1 2 -246288
4 5 -222742
3 1 246288
3 4 947824
5 2 -178721
4 3 -947824
5 4 756570
2 5 707902
5 1 36781
Sample Output 3
-449429
解题思路
之前在洛谷上做过类似的题目:P8733 [蓝桥杯 2020 国 C] 补给,所以比赛的时候一下就做出来了。
看到数据范围容易想到状压 dp,定义 表示从起点走到当前点 且经过的节点所表示的二进制集合为 的所有路径中权值的最小值,状态转移方程为 。其中 指 的最短距离,任意两点间的最短路可以通过 Floyd 算法求得。另外 所经过的中间节点(不含 和 )不会被加入到集合中,这里每次只会加入最后到达的节点。
为什么这样做是正确的呢?首先上面的做法本质是找到一个 的排列 ,使得 最小,这条路径必然会经过每个节点至少一次。而最优解所对应的路径中,节点 也必然出现至少一次,对于每个编号的节点从中任选出一个(路径的起点和终点必选),那么整个路径就会被分成 段,显然每一段的长度必然是对应两端点的最短路径,否则就可以用更短的路径来替换,与最优解矛盾了。
最后的答案为 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
const int N = 25, M = 1 << 20;
int g[N][N];
int f[M][N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
memset(g, 0x3f, sizeof(g));
for (int i = 1; i <= n; i++) {
g[i][i] = 0;
}
while (m--) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
u--, v--;
g[u][v] = min(g[u][v], w);
}
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
}
}
memset(f, 0x3f, sizeof(f));
for (int i = 0; i < n; i++) {
f[1 << i][i] = 0;
}
for (int i = 1; i < 1 << n; i++) {
for (int j = 0; j < n; j++) {
if (i >> j & 1) {
for (int k = 0; k < n; k++) {
if (j == k) continue;
if (i >> k & 1) {
f[i][j] = min(f[i][j], f[i ^ 1 << j][k] + g[k][j]);
}
}
}
}
}
int ret = 0x3f3f3f3f;
for (int i = 0; i < n; i++) {
ret = min(ret, f[(1 << n) - 1][i]);
}
if (ret >= 0x3f3f3f3f >> 1) printf("No");
else printf("%d", ret);
return 0;
}
ps:这题还可以建分层图然后跑 spfa,一开始用 std::array<int, 2>
来存二元组结果 TLE 了,然后改成 std::pair<int, int>
刚好卡过去,最慢的数据跑了 ,挺哈人的()
AC 代码如下,最坏情况下的时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 25, M = 1 << 20;
int g[N][N];
int dist[M][N];
bool vis[M][N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
memset(g, 0x3f, sizeof(g));
while (m--) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
u--, v--;
g[u][v] = min(g[u][v], w);
}
queue<PII> q;
memset(dist, 0x3f, sizeof(dist));
for (int i = 0; i < n; i++) {
q.push({1 << i, i});
dist[1 << i][i] = 0;
vis[1 << i][i] = true;
}
while (!q.empty()) {
auto p = q.front();
q.pop();
vis[p.first][p.second] = false;
for (int i = 0; i < n; i++) {
if (dist[p.first | 1 << i][i] > dist[p.first][p.second] + g[p.second][i]) {
dist[p.first | 1 << i][i] = dist[p.first][p.second] + g[p.second][i];
if (!vis[p.first | 1 << i][i]) {
vis[p.first | 1 << i][i] = true;
q.push({p.first | 1 << i, i});
}
}
}
}
int ret = 0x3f3f3f3f;
for (int i = 0; i < n; i++) {
ret = min(ret, dist[(1 << n) - 1][i]);
}
if (ret >= 0x3f3f3f3f >> 1) printf("No");
else printf("%d", ret);
return 0;
}
参考资料
Editorial - AtCoder Beginner Contest 338:https://atcoder.jp/contests/abc338/editorial/9180
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17998057
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-01-30 F. Timofey and Black-White Tree