[每日一题] 观光奶牛

116 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

观光奶牛

给定一张 L 个点、P 条边的有向图,给定一张 L 个点、P 条边的有向图,

每个点都有一个权值 f[i],每条边都有一个权值 t[i]每个点都有一个权值 f[i],每条边都有一个权值 t[i]。

求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。

输出这个最大值。输出这个最大值。

注意:数据保证至少存在一个环。注意:数据保证至少存在一个环。

输入格式

第一行包含两个整数 L 和 P。

接下来 L 行每行一个整数,表示 f[i]。

再接下来 P 行,每行三个整数 ab,t[i],表示点 a 和 b 之间存在一条边,边的权值为 t[i]

输出格式

输出一个数表示结果,保留两位小数。

数据范围

  • 2 ≤ L ≤ 1000
  • 2 ≤ P ≤ 5000
  • 1 ≤ f[i], t[i] ≤ 1000

思路

spfa + 二分 + 01分数规划

ansans 为 最大值

求:

i=1kf[i]i=1kt[i]ans\frac {\sum ^ k _ {i = 1} f[i] } {\sum ^ k _ {i = 1} t[i]} \leq ans

等价于:

i=1kf[i]t[i]ans0\sum ^ k _ {i = 1} f[i] - t[i] * ans \leq 0

则:

i=1kt[i]ansf[i]0\sum ^ k _ {i = 1} t[i]*ans - f[i] \geq 0

二分ans 判负环是否存在

如果 存在,那么证明原式成立 范围缩小到 [ans,r][ans, r]

如果 不存在, 那么证明原式不对 范围缩小到 [l,ans][l, ans]

最坏情况存在长度为 LL 的环, ti=L,fi=1000\sum t_i= L,\sum f_i=1000。 答案最大可能是 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;
}