其他分享
首页 > 其他分享> > P1768 天路

P1768 天路

作者:互联网

一种完全使用裸 SPFA 算法的做法。

一道分数规划题,实际上是最短路的灵活运用。

题意

题目话很多,简单来说就是:

\(n\) 个点,\(m\) 条边,每条边有两个属性,分别为花费 \(\text{cost}\) 和收益 \(\text{joy}\)。求出一个环使得环上的 \(\dfrac{\sum\text{joy}}{\sum \text{cost}}\) 的值最大。

思路

如果我们令答案为 \(p\),则对于图上任意一个环,都有:

\[\dfrac{\sum\text{joy}}{\sum \text{cost}}\le p \]

将不等式左右两边乘上 \(\sum\text{cost}\),则:

\[\sum\text{joy}\le p\times\sum\text{cost} \]

换个位置可得:

\[p\times \sum\text{cost}-\sum\text{joy}\ge 0 \]

也就是说,\(p\) 是正确的答案时,图上不会出现负环。

对于第 \(i\) 条边而言,在跑 SPFA 算法的时候,他实际的边权应该是

\[p\times \text{cost}_i-\text{joy}_i \]

另外,我们可以发现,题目要求的是一个式子的值最大。观察 \(p\times \sum\text{cost}-\sum\text{joy}\) 发现这个式子是具有单调性的,那么就可以使用二分减少计算次数(从 \(200\) 次计算下降到了 \(\log_2(200)\approx8\) 次)

而且,在这道题中,题目并没有告诉我们起点是那个点。因此,我们需要建立一个超级源点(我的代码中是 \(0\) 号点),然后将他与其他 \(n\) 个点连接一条不会对答案产生影响的边。

最后,我罗列一下需要注意的几个点:


你以为结束了?并没有。

当你交上上面思路写出的代码时,你会发现你只有 90 分。

上面的思路唯一可以再减少操作步数的就只有 SPFA 内部了。我们设置一个值 \(\text{delta}\) 表示当一个点入队次数上限,进行人肉二分,发现当 \(\text{delta}\) 等于 \(10\) 的时候就可以通过此题,而且跑的飞快,总共只有 67ms,成功进入最优解第一页。

代码

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<iomanip>
using namespace std;

const int maxn = 7010;
const double eps = 1e-3;

int n, m;

struct node {
    int v, joy, cost;
    node(int vv, int jjoy, int ccost) {
        v = vv, joy = jjoy, cost = ccost;
    }
};

std::vector<node> g[maxn];

void add(int u, int v, int joy, int cost) {
    g[u].push_back(node(v, joy, cost));
}

double d[maxn];
bool inqueue[maxn];
int cnt[maxn];

const int delta = 30;

bool spfa(double p) {
    for (int i = 0; i < maxn; i++) {
        d[i] = 1e9;
    }
    memset(inqueue, 0, sizeof(inqueue));
    memset(cnt, 0, sizeof(cnt));
    queue<int> q;
    q.push(0);
    d[0] = 0;
    cnt[0]++;
    inqueue[0] = true;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inqueue[u] = false;
        if (cnt[u] > delta) {
            return true;
        }
        for (int i = 0; i < g[u].size(); i++) {
            int v = g[u][i].v;
            if (d[v] > d[u] + p * g[u][i].cost - g[u][i].joy) {
                d[v] = d[u] + p * g[u][i].cost - g[u][i].joy;
                if (!inqueue[v]) {
                    inqueue[v] = true;
                    q.push(v);
                    cnt[v]++;
                    if (cnt[v] > delta) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, joy, cost;
        cin >> u >> v >> joy >> cost;
        add(u, v, joy, cost);
    }
    for (int i = 1; i <= n; i++) {
        add(0, i, 0, 0);
    }
    double l = 0, r = 200.0;
    while (l + eps < r) {
        double mid = (l + r) / 2.0;
        if (spfa(mid)) {
            l = mid;
        } else {
            r = mid;
        }
    }
    if (l == 0) {
        cout << -1 << endl;
    } else {
        cout << fixed << setprecision(1) << l << endl;
    }
    return 0;
}

标签:cnt,P1768,int,text,sum,cost,joy
来源: https://www.cnblogs.com/luogu-int64/p/16270049.html