携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
观光奶牛
输入格式
第一行包含两个整数 L 和 P。
接下来 L 行每行一个整数,表示 f[i]。
再接下来 P 行,每行三个整数 a,b,t[i],表示点 a 和 b 之间存在一条边,边的权值为 t[i]。
输出格式
输出一个数表示结果,保留两位小数。
数据范围
2 ≤ L ≤ 10002 ≤ P ≤ 50001 ≤ f[i], t[i] ≤ 1000
思路
spfa + 二分 + 01分数规划
设 为 最大值
求:
等价于:
则:
二分ans 判负环是否存在
如果 存在,那么证明原式成立 范围缩小到
如果 不存在, 那么证明原式不对 范围缩小到
最坏情况存在长度为 的环, 。 答案最大可能是 1000
代码
#include <bits/stdc++.h>
const int N = 1111, M = 1e4 + 111;
using namespace std;
int n, m, idx, h[N], v[N], cnt[N];
double f[N], d[N];
struct E {
int v, n, w;
} g[M];
void add (int a, int b, int c) {
g[idx].v = b; g[idx].n = h[a];
g[idx].w = c; h[a] = idx ++;
}
bool spfa (double val) {
queue<int> que;
for (int i = 1; i <= n; ++ i)
que.push(i), v[i] = 1, d[i] = cnt[i] = 0;
while (que.size()) {
auto t = que.front();
que.pop();
v[t] = 0;
for (int i = h[t]; i; i = g[i].n) {
int j = g[i].v;
double w = g[i].w * val - f[t] + d[t];
if (d[j] > w) {
d[j] = w;
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) return true;
if (!v[j]) que.push(j), v[j] = 1;
}
}
}
return false;
}
int main() {
idx = 1;
cin >> n >> m;
for (int i = 1; i <= n; ++ i) cin >> f[i];
for (int i = 1; i <= m; ++ i) {
int a, b, c;
cin >> a >> b >> c;
add (a, b, c);
}
double l = 1, r = 1000;
for (int i = 1; i <= 50; ++ i) {
double mid = (l + r) / 2;
if (spfa(mid)) l = mid;
else r = mid;
}
printf ("%.2lf\n", l);
return 0;
}