Luogu P3305 [SDOI2013]费用流 二分 网络流

Posted maomao9173

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3305 [SDOI2013]费用流 二分 网络流相关的知识,希望对你有一定的参考价值。

题目链接 (Click) (Here)

非常有趣的一个题目。

关键结论:所有的单位费用应该被分配在流量最大的边上。

即:在保证最大流的前提下,使最大流量最小。这里我们采用二分的方法,每次判断让所有边的流量(<=mid)时是否依然有最大流,求得最小的最大流量(*p)即可。

为什么会有实数流量呢?其实我也不懂,不过这也造成这个题目需要把流量改成(double),有很多细节需要小心谨慎。。。

#include <bits/stdc++.h>
using namespace std;

const int N = 400010;
const int M = 400010;
const int INF = 0x3f3f3f3f;

int u[N], v[N], flow[N]; double f[N];

int n, m, p, cnt = -1, head[N];

struct edge {
    int nxt, to; double f;
}e[M];

void add_edge (int from, int to, double flw) {
    e[++cnt].nxt = head[from];
    e[cnt].to = to;
    e[cnt].f = flw;
    head[from] = cnt;
}

void add_len (int u, int v, double f) {
    add_edge (u, v, f);
    add_edge (v, u, 0);
}

queue <int> q;
int cur[N], deep[N];

bool bfs (int s, int t) {
    memcpy (cur, head, sizeof (head));
    memset (deep, 0x3f, sizeof (deep));
    deep[s] = 0; q.push (s);
    while (!q.empty ()) {
        int u = q.front (); q.pop ();
        for (int i = head[u]; ~i; i = e[i].nxt) {
            int v = e[i].to;
            if (deep[v] == INF && fabs (e[i].f) > 1e-8) {
                deep[v] = deep[u] + 1;
                q.push (v);
            }
        }
    }
    return deep[t] != INF;
}

double dfs (int u, int t, double lim) {
    if (u == t || fabs (lim) < 1e-8) {
        return lim;
    }
    double tmp = 0, flow = 0;
    for (int &i = cur[u]; ~i; i = e[i].nxt) {
        int v = e[i].to;
        if (deep[v] == deep[u] + 1) {
            tmp = dfs (v, t, min (lim, e[i].f));
            lim -= tmp;
            flow += tmp;
            e[i ^ 0].f -= tmp;
            e[i ^ 1].f += tmp;
            if (fabs (lim) < 1e-8) break;
        }
    }
    return flow;
} 

double Dinic (int s, int t) {
    double res = 0;
    while (bfs (s, t)) {
        res += dfs (s, t, INF);
    }
    return res;
}

double max_flow;

bool can_use (double flw) {
    cnt = -1; int s = 1, t = n;
    memset (head, -1, sizeof (head));
    for (int i = 1; i <= m; ++i) {
        add_len (u[i], v[i], min (f[i], flw));
    }
    return fabs (Dinic (s, t) - max_flow) < 1e-8;
}

int main () {
    memset (head, -1, sizeof (head));
    cin >> n >> m >> p;
    for (int i = 1; i <= m; ++i) {
        cin >> u[i] >> v[i] >> f[i];
        add_len (u[i], v[i], f[i]);
    }
    int s = 1, t = n; max_flow = Dinic (s, t);
    printf ("%.0lf
", max_flow);
    double l = 0, r = INF;
    while (r - l > 1e-8) {
        double mid = (l + r) / 2.0;
        if (can_use (mid)) {
            r = mid;
        } else {
            l = mid;
        }
    }
    printf ("%.4lf
", r * p);
} 

以上是关于Luogu P3305 [SDOI2013]费用流 二分 网络流的主要内容,如果未能解决你的问题,请参考以下文章

题解[SDOI2013]费用流

Luogu P4068 [SDOI2016]数字配对(费用流)

luogu P2153 [SDOI2009]晨跑 |费用流

BZOJ 3130: [Sdoi2013]费用流 网络流+二分

BZOJ 3130 Sdoi2013 费用流

BZOJ3130: [Sdoi2013]费用流[最大流 实数二分]