AcWing361. 观光奶牛 题解 二分(分数规划)+SPFA判负环

题目链接:https://www.acwing.com/problem/content/363/

解题思路:

首先,假设 \(\frac{ \sum f_i }{ \sum t_i } \gt x\)

\(\sum ( f_i - t_i \times x ) \gt 0\)

\(\sum ( t_i \times x - f_i ) \lt 0\)

也就是说,如果原图中第 \(i\) 条边从节点 \(a\) 连向 \(b\),边权为 \(t_i\),则新图中从 \(a\)\(b\) 连一条边权为 \(t_i \times x - f_b\) 的边。

并判断图中是否存在负环,如果存在负环,则比例至少大于 \(x\)

可以发现可以通过 二分答案 得到最大的比例。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5, maxm = 5e3 + 5;
int n, m, f[maxn], num[maxn];
double dis[maxn];
bool ins[maxn];
struct Edge {
    int v;
    double w;
};
vector<Edge> g[maxn];
int a[maxm], b[maxm], t[maxm];

void init(double x) {
    for (int i = 1; i <= n; i++)
        g[i].clear(), num[i] = dis[i] = 0;
    for (int i = 0; i < m; i++)
        g[ a[i] ].push_back({ b[i], t[i] * x - f[b[i]] });
}

bool spfa() {
    stack<int> stk;
    for (int i = 1; i <= n; i++) {
        ins[i] = true;
        stk.push(i);
    }
    while (!stk.empty()) {
        int u = stk.top();
        stk.pop();
        ins[u] = false;
        for (auto &[v, w] : g[u]) {
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!ins[v]) {
                    if (++num[v] >= n + 1)
                        return false;
                    ins[v] = true;
                    stk.push(v);
                }
            }
        }
    }
    return true;
}

bool check(double x) {
    init(x);
    return spfa();
}

double cal() {
    double l = 0, r = 1e3;
    while (r - l > 1e-3) {
        double mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid;
    }
    return l;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", f+i);
    for (int i = 0; i < m; i++)
        scanf("%d%d%d", a+i, b+i, t+i);
    printf("%.2lf\n", cal());
    return 0;
}
posted @ 2024-10-21 16:05  quanjun  阅读(4)  评论(0编辑  收藏  举报