[BZOJ1834][ZJOI2010]网络扩容(最大流+费用流)

第一问即为模板最大流。
第二问在跑完最大流的残余网络上继续建图,对于没有满流的边,将其费用置为 0 。同时对于所有的边<u,v>,再建一条边 <u,v> <script type="math/tex" id="MathJax-Element-3"> </script>,容量为 ,费用为边 <u,v> <script type="math/tex" id="MathJax-Element-5"> </script>的扩容费用。
同时,还需要建一个新的源点连向原图中的源点,容量为 K ,费用为0
这样,最小的扩容费用就是最小费用最大流。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5, INF = 0x3f3f3f3f;
int n, m, ecnt = 1, nxt[N], adj[N], go[N], cap[N], dis[N], lev[N],
yd = 1, hd, len, que[N], cost[N], Ans, K, Y[N], st[N], Cap[N];
bool vis[N], walk[N];
void add_edge(int u, int v, int w, int x) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    cap[ecnt] = w; cost[ecnt] = x; st[ecnt] = u;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
    cap[ecnt] = 0; cost[ecnt] = -x; st[ecnt] = v;
}
bool bfs() {
    int i; for (i = yd; i <= hd; i++) lev[i] = -1;
    que[len = 1] = yd; lev[yd] = 0;
    for (i = 1; i <= len; i++) {
        int u = que[i];
        for (int e = adj[u], v; e; e = nxt[e])
            if (cap[e] > 0 && lev[v = go[e]] == -1) {
                lev[v] = lev[u] + 1; que[++len] = v;
                if (v == hd) return 1;
            }
    }
    return 0;
}
int dinic(int u, int flow) {
    if (u == hd) return flow;
    int res = 0, d;
    for (int e = adj[u], v; e; e = nxt[e])
        if (cap[e] > 0 && lev[u] < lev[v = go[e]]) {
            d = dinic(v, min(cap[e], flow - res));
            if (d) {
                cap[e] -= d; cap[e ^ 1] += d;
                res += d; if (res == flow) break; 
            }
        }
    if (res != flow) lev[u] = -1;
    return res;
}
int solve() {
    int ans = 0; while (bfs()) ans += dinic(yd, INF); return ans;
}
bool bfs2() {
    que[len = 1] = yd; int i;
    for (i = 1; i <= yd; i++) dis[i] = INF, walk[i] = 0;
    vis[yd] = 1; dis[yd] = 0;
    for (i = 1; i <= len; i++) {
        int u; vis[u = que[i]] = 0;
        for (int e = adj[u], v; e; e = nxt[e])
            if (cap[e] > 0 && dis[u] + cost[e] < dis[v = go[e]]) {
                dis[v] = dis[u] + cost[e];
                if (!vis[v]) vis[v] = 1, que[++len] = v;
            }
    }
    return dis[hd] < INF;
}
int dinic2(int u, int flow) {
    if (u == hd) return Ans += flow * dis[hd], flow;
    walk[u] = 1; int res = 0, delta = 0;
    for (int e = adj[u], v; e; e = nxt[e])
        if (cap[e] > 0 && !walk[v = go[e]]
        && dis[u] + cost[e] == dis[v]) {
            delta = dinic2(v, min(cap[e], flow - res));
            if (delta) {
                cap[e] -= delta; cap[e ^ 1] += delta;
                res += delta; if (res == flow) break;
            }
        }
    return res;
}
int solve2() {
    Ans = 0; while (bfs2()) dinic2(yd, INF); return Ans;
}
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
int main() {
    int i, u, v, x, y, ff; n = hd = read(); m = read(); K = read();
    for (i = 1; i <= m; i++) u = read(), v = read(), x = read(),
        y = read(), add_edge(u, v, x, y), Cap[ecnt - 1] = x;
    printf("%d ", solve()); ff = ecnt;
    for (i = 2; i <= ff; i += 2) {
        if (cap[i] > 0) add_edge(st[i], go[i], cap[i], 0),
            cap[ecnt] = cap[i ^ 1]; cap[i] = INF; cap[i ^ 1] = 0;
    }
    yd = hd + 1; add_edge(yd, 1, K, 0); printf("%d\n", solve2());
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值