负环的习题和应用
大家好,我是Weekoder!
今天要讲的是负环的一些习题与负环在题目中的应用。
一共有
拉近距离
题面
在小明和小红的生活中,有
这些节点构成了一个网络,其中节点 Forever love
。
分析
可以把题意理解为一个人在一条路径上不断往另一个人的方向走。
注意到题目中说距离会减少
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
struct Edge {
int to, w;
};
int n, m, dis[N], dp[N];
bool vis[N];
vector<Edge> nbr[N];
bool spfa(int s) {
queue<int> q;
memset(vis, 0, sizeof vis);
memset(dis, 0x3f, sizeof dis);
memset(dp, 0, sizeof dp);
dis[s] = 0;
vis[s] = 1;
q.push(s);
while (!q.empty()) {
int cur = q.front(); q.pop();
vis[cur] = 0;
for (auto nxt : nbr[cur]) {
int to = nxt.to, w = nxt.w;
if (dis[to] > dis[cur] + w) {
dis[to] = dis[cur] + w;
dp[to] = dp[cur] + 1;
if (dp[to] >= n) return 1;
if (!vis[to]) {
vis[to] = 1;
q.push(to);
}
}
}
}
return 0;
}
int main() {
cin >> n >> m;
for (int i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w;
nbr[u].emplace_back((Edge){v, -w});
}
if (spfa(1)) {
cout << "Forever love";
return 0;
}
int tmp = dis[n];
if (spfa(n)) {
cout << "Forever love";
return 0;
}
cout << min(dis[1], tmp);
return 0;
}
Sightseeing Cows G
题面
给你一张
找一个环,设环上的点组成的集合为
分析
考虑二分答案。
设二分答案
推到这里,我们发现了一个有趣的结论:若想继续猜大些,则需要满足对于环上的边权之和,有
在这里,就仅仅需要判断是否有负环并二分答案,推荐使用 DFS-SPFA,用时仅有
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
const double eps = 1e-6;
struct Edge {
int to;
double w;
};
int n, m;
double dis[N], f[N];
bool vis[N];
vector<Edge> nbr[N];
bool spfa(int cur, double X) {
vis[cur] = 1;
for (auto nxt : nbr[cur]) {
int to = nxt.to;
double w = nxt.w * X - f[cur];
if (dis[to] > dis[cur] + w) {
dis[to] = dis[cur] + w;
if (vis[to] || spfa(to, X)) return 1;
}
}
vis[cur] = 0;
return 0;
}
bool check(double X) {
memset(vis, 0, sizeof vis);
memset(dis, 0, sizeof dis);
for (int i = 1; i <= n; i++)
if (spfa(i, X))
return 1;
return 0;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> f[i];
double l = 1005, r = -1;
for (int i = 1, u, v; i <= m; i++) {
double w;
cin >> u >> v >> w;
nbr[u].emplace_back((Edge){v, w});
l = min(l, w);
r = max(r, w);
}
r++, l--;
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.2lf", l);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
const double eps = 1e-6;
struct Edge {
int to;
double w;
};
int T, n, m, dp[N];
double dis[N], f[N];
bool vis[N];
vector<Edge> nbr[N];
bool spfa(double X) {
queue<int> q;
memset(vis, 0, sizeof vis);
memset(dis, 0, sizeof dis);
memset(dp, 0, sizeof dp);
for (int i = 1; i <= n; i++)
vis[i] = 1, q.push(i);
while (!q.empty()) {
int cur = q.front(); q.pop();
vis[cur] = 0;
for (auto nxt : nbr[cur]) {
int to = nxt.to;
double w = X * nxt.w - f[cur];
if (dis[to] > dis[cur] + w) {
dis[to] = dis[cur] + w;
dp[to] = dp[cur] + 1;
if (dp[to] >= n) return 1;
if (!vis[to]) {
vis[to] = 1;
q.push(to);
}
}
}
}
return 0;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> f[i];
double l = 1005, r = -1;
for (int i = 1, u, v; i <= m; i++) {
double w;
cin >> u >> v >> w;
nbr[u].emplace_back((Edge){v, w});
l = min(l, w);
r = max(r, w);
}
double tmp = r + 1;
r++, l--;
while (r - l > eps) {
double mid = (l + r) / 2;
if (spfa(mid)) l = mid;
else r = mid;
}
printf("%.2lf", l);
return 0;
}
最小圈
题面
考虑带权有向图
即
给定图
题面概括
给定一个图,找一个环,最小化
分析
考虑二分答案。
设二分答案
于是我们又得到了:将边权看作原边权
这个题需要用 DFS-SPFA,否则会
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5;
const double eps = 1e-9;
struct Edge {
int to;
double w;
};
int n, m;
double dis[N];
bool vis[N];
vector<Edge> nbr[N];
bool spfa(int cur, double X) {
vis[cur] = 1;
for (auto nxt : nbr[cur]) {
int to = nxt.to;
double w = nxt.w - X;
if (dis[to] > dis[cur] + w) {
dis[to] = dis[cur] + w;
if (vis[to] || spfa(to, X)) return 1;
}
}
vis[cur] = 0;
return 0;
}
bool check(double X) {
memset(dis, 0, sizeof dis);
memset(vis, 0, sizeof vis);
for (int i = 1; i <= n; i++)
if (spfa(i, X)) return 1;
return 0;
}
int main() {
cin >> n >> m;
double l = 1e8, r = -1e8;
for (int i = 1, u, v; i <= m; i++) {
double w;
cin >> u >> v >> w;
nbr[u].emplace_back((Edge){v, w});
l = min(l, w);
r = max(r, w);
}
r++, l--;
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
printf("%.8lf", r);
return 0;
}
小结
以上就是关于负环的三个习题的内容。负环是一个判定型的算法,适合搭配二分答案等算法,发挥更大的用处。今天的习题都有一些难度,需要巩固练习。
再见!
本文作者:Weekoder
本文链接:https://www.cnblogs.com/Weekoder/p/18240228
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步