# POJ3621_Sightseeing Cows

题意:


给定一张有向图,每个点有权值 \(fun[i]\) ,每条边有权值 \(time[i]\)

要求找出一个环,使得环上所有点的点权和除以所有边的边权和最大

解:


首先,显然,这是一道01分数规划题

参照分数规划的套路

假定当前二分的值为 \(mid\) ,有环 \(S=(\{v\},\ \{e\})\) ,环有 \(t\) 个点和 \(t\) 条边

考虑是否存在 \(S\) 使得

\[\sum_{i=1}^{t}(mid*time_{i}-fun_{i})<0 \\ 即\quad\exists S=(\{v\},\ \{e\})\quad使得\quad mid<\frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}} \]

此时答案应该大于 \(mid\)

若不存在这样的 \(S\) ,则

\[\forall S,\quad mid\geqslant \frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}} \]

此时答案应该小于等于 \(mid\)

问题又来了,如何判断是否存在 \(S\)

有这样的办法:

在每次选定 \(mid\) 之后

新建一个与原图一样的图,但是没有点权,只有边权,并对边权值做如下修改

\(\forall e_{i}=edge(x\ ->\ y),\quad weight(x\ ->\ y)=mid*time_{e_{i}}-fun_{x}\)

这样建图有什么好处?

这样,若要判定 \(\sum_{i=1}^{t}(mid*time_{i}-fun_{i})<0\) ,刚好对应图中存在“负环”

由此,在新建的图上跑SPFA,有负环说明 \(mid<\frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}\)

没有则说明 \(mid\geqslant \frac{\sum_{i=1}^{t}fun_{i}}{\sum_{i=1}^{t}time_{i}}\)

代码:


#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int M = 5010;
int n, m;
double l, r, mid, eps;
int head[N], nxt[M], ver[M], tot;
int thead[N], tnxt[M], tver[M], ttot;
double tedge[M], edge[M], fun[N];

void add(int u, int v, double w) { 
    ver[++tot] = v; edge[tot] = w; 
    nxt[tot] = head[u]; head[u] = tot;
}
void tadd(int u, int v, double w) {
    tver[++ttot] = v; tedge[ttot] = mid * w - fun[u];
    tnxt[ttot] = thead[u]; thead[u] = ttot;
}

void build() {
    memset(thead, 0, sizeof thead);
    memset(tnxt, 0, sizeof tnxt);
    memset(tver, 0, sizeof tver);
    memset(tedge, 0, sizeof tedge);
    ttot = 0;
    for (int i = 1; i <= n; i++) {
        int x = i;
        for (int j = head[x]; j; j = nxt[j]) {
            int y = ver[j];
            tadd(x, y, edge[j]);
        }
    }
}

queue<int> q;
double dis[N]; int cnt[N]; bool vis[N];
bool check() {
    while (q.size()) q.pop();
    for (int i = 1; i <= n; i++) {
        vis[i] = true;
        q.push(i);
        dis[i] = cnt[i] = 0;
    }
    while (!q.empty()) {
        int x = q.front(); q.pop();
        vis[x] = false;
        for (int i = thead[x]; i; i = tnxt[i]) {
            int y = tver[i];
            double z = tedge[i];
            if (dis[y] > dis[x] + z) {
                dis[y] = dis[x] + z;
                if (!vis[y]) {
                    q.push(y); vis[y] = true;
                }
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n) return true;
            }
        }
    }
    return false;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%lf", &fun[i]);
    for (int i = 1; i <= m; i++) {
        int u, v; double w;
        scanf("%d%d%lf", &u, &v, &w);
        add(u, v, w);
    }

    l = 0; r = 1000; eps = 0.000000000001;
    while (l + eps < r) {
        mid = (l + r) / 2;
        build();
        if (check()) l = mid;
        else r = mid;
    }
    printf("%.2lf", l);
    return 0;
}
posted @ 2020-07-29 08:26  熹圜  阅读(85)  评论(0)    收藏  举报