P2604:「ZJOI2010」网络扩容题解

解法

大水题。

考虑按照给定数据建一张费用全部为 0 的图。跑完费用流求最大流输出答案之后,在现有的边的基础上加一条方向一致、容量无限、费用为\(w_i\)的边,最后开一个新点连接点\(n\),容量为\(k\)即可。

代码

// P2604.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int MAX_N = 1010, MAX_M = 5050, INF = 0x3f3f3f3f;

int head[MAX_N], current, flow[MAX_N], dist[MAX_N], s, t, pre[MAX_N];
int n, m, k, ai[MAX_M], bi[MAX_M], ci[MAX_M], di[MAX_M];
bool vis[MAX_N];

struct edge
{
    int to, nxt, weight, cost;
} edges[MAX_M << 2];

void addpath(int src, int dst, int weight, int cst)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].weight = weight, edges[current].cost = cst;
    head[src] = current++;
}

void add(int src, int dst, int weight, int cst)
{
    addpath(src, dst, weight, cst);
    addpath(dst, src, 0, -cst);
}

bool spfa()
{
    memset(dist, 0x3f, sizeof(dist));
    queue<int> q;
    q.push(s), vis[s] = true;
    flow[s] = INF, dist[s] = 0;
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = edges[i].nxt)
            if (edges[i].weight > 0 && dist[edges[i].to] > dist[u] + edges[i].cost)
            {
                dist[edges[i].to] = dist[u] + edges[i].cost;
                flow[edges[i].to] = min(flow[u], edges[i].weight);
                pre[edges[i].to] = i;
                if (!vis[edges[i].to])
                    vis[edges[i].to] = true, q.push(edges[i].to);
            }
    }
    return dist[t] != INF;
}

pair<ll, ll> mcmf()
{
    ll ans1 = 0, ans2 = 0;
    while (spfa())
    {
        ans1 += flow[t], ans2 += flow[t] * dist[t];
        int pt = t, side = pre[pt];
        while (pt != s)
        {
            edges[side].weight -= flow[t], edges[side ^ 1].weight += flow[t];
            pt = edges[side ^ 1].to, side = pre[pt];
        }
    }
    return make_pair(ans1, ans2);
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d%d", &ai[i], &bi[i], &ci[i], &di[i]);
    // first procedure:
    for (int i = 1; i <= m; i++)
        add(ai[i], bi[i], ci[i], 0);
    s = 1, t = n;
    pair<ll, ll> ans = mcmf();
    printf("%lld ", ans.first);
    // reset for the second procedure:
    for (int i = 1; i <= m; i++)
        add(ai[i], bi[i], INF, di[i]);
    s = 1, t = n + 1;
    add(n, t, k, 0);
    ans = mcmf();
    printf("%lld", ans.second);
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *