题目大意
有个 $n$ 个点,$m$ 条边的有向图,点有点权,边有边权。
现在要找出一个环,使得点权和与边权和的比值最大。
思路
既然说要使得点权和与边权和的比值最大,那么就会想到 $01$ 分数规划。二分答案就不用说了,重点是这个 $check$ 函数。
$01$ 分数规划的板子中要检查的是你选出来的最优的那几个 $a_i-b_i \cdot p$ 之和是否大于 $0$。
那么我们类比一下,想想现在怎么检查:
- 我们可以寻找环,看看所选环中 $a_i-b_i \cdot p$ 的之和是否大于 $0$。
- 更简单一点的方式:转换为负权环问题(其实就是把权值取反)。
- 然后 板子题 $*$ $2$ !
伪代码/框架
浮点数二分(想想精度是多少)
$check()$:
- 跑负权环板子(想想权值怎么算)
- 注意:每个点都是起点
代码
#include <cstdio> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <cstring> #include <cmath> using namespace std; const double EPS = 1e-4; // 精度问题 int n, m; double f[1010]; // 浮点数 double dis[1010]; // 浮点数,千万别忘了 int vis[1010]; // 记录是否在队列里 int sum[1010]; // 入队次数 struct edge // 边 { int y, w; } ; vector<edge> g[1010]; // 图 void add(int x, int y, int w) // 建边 { g[x].push_back((edge){y, w}); } bool check(double mid) // 检查 { queue<int> q; memset(dis, 0, sizeof(dis)); // 每个点都是起点 memset(vis, 0, sizeof(vis)); memset(sum, 0, sizeof(sum)); for (int i = 1; i <= n; i++) { q.push(i); // 都要入队 vis[i] = 1; } while (q.size()) { int x = q.front(); q.pop(); vis[x] = 0; for (int i = 0; i < g[x].size(); i++) { int y = g[x][i].y; int w = g[x][i].w; if (dis[y] > dis[x] + mid * w - f[x]) // 权值 { dis[y] = dis[x] + mid * w - f[x]; if (vis[y] == 0) { q.push(y); vis[y] = 1; sum[y]++; if (sum[y] >= n) return true; // 存在负权值情况下的负权环(即正权环) } } } } return false; // 不存在 } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> f[i]; for (int i = 1; i <= m; i++) { int x, y, w; cin >> x >> y >> w; add(x, y, w); } double l = 0, r = 1000; // 浮点数二分 while (r - l > EPS) // 二分 { double mid = (l + r) / 2; if (check(mid)) l = mid; else r = mid; } printf("%.2lf\n", l); // 输出 return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!