[图论与代数结构 601] 最小费用最大流 题解

发布时间 2023-09-02 23:57:24作者: xvl

题目传送门

一道网络流题。

费用流板子题。费用流实际上是在给最大流套个最短路,而费用流一般边权会有负数,所以用 SPFA 算法,关于 SPFA,它复活了

可以在最大流做 bfs 的时候将 SPFA 套上去。数据似乎比较凶残,建议使用 Dinic 来求最大流。

Code

#include <bits/stdc++.h>

namespace xvl_ { 
    #define ll long long
    #define IMAX 1e9
    #define LMAX 1e18
    void debug() { std :: cerr << "debug" << "\n"; } 
    template <typename T> inline T Max(T a, T b) { return a > b ? a : b; }
    template <typename T> inline T Min(T a, T b) { return a < b ? a : b; }
    template <typename T, typename... Args> inline T Max(T a, Args... args) { return a > Max(args...) ? a : Max(args...); }
    template <typename T, typename... Args> inline T Min(T a, Args... args) { return a < Min(args...) ? a : Min(args...); }
}
using namespace std;
using namespace xvl_;
struct Edge { ll to, r, c, cp; };
ll n, m, s, t, ans1, ans2;
ll dis[405], vis[405];
bool inq[405], isz[405];
vector <Edge> G[405];
void Add_Edge(ll u, ll v, ll c, ll w) {
    G[u].push_back({v, c, w, (int)(G[v].size())});
    G[v].push_back({u, 0, -w, (int)(G[u].size() - 1)});
}
bool check(ll s, ll t) { 
    queue <ll> q;
    fill(dis + 1, dis + 1 + 400, LMAX);
    dis[s] = 0, inq[s] = 1, q.push(s);
    while (!q.empty()) {
        ll cur = q.front();
        q.pop();
        inq[cur] = 0;
        for (auto v : G[cur]) {
            if (dis[cur] + v.c < dis[v.to] and v.r) {
                dis[v.to] = dis[cur] + v.c;
                if (!inq[v.to]) inq[v.to] = 1, q.push(v.to);
            }
        }
    }
    return dis[t] != LMAX;
}
ll aug(ll cur, ll now, ll& c) {
    ll flow = 0;
    if (cur == t) return now;
    isz[cur] = 1;
    for (ll& i = vis[cur]; i < G[cur].size(); i++) {
        Edge& v = G[cur][i];
        if (dis[v.to] != dis[cur] + v.c or isz[v.to] or !v.r) continue;
        int d = aug(v.to, Min(now - flow, v.r), c);
        v.r -= d, G[v.to][v.cp].r += d, flow += d, c += v.c * d;
        if (flow == now) break;
    }
    isz[cur] = 0;
    return flow;
}
int main() {
    /*
    freopen("InName.in", "r", stdin);
    freopen("OutName.out", "w", stdout);
    */
    ios :: sync_with_stdio(0);
    cin >> n >> m;
    s = 1, t = n;
    for (int i = 1; i <= m; i++) {
        ll u, v, c, w;
        cin >> u >> v >> c >> w;
        Add_Edge(u, v, c, w);
    }
    while (check(s, t)) {
        memset(vis, 0, sizeof vis);
        ans1 += aug(s, LMAX, ans2);
    }
    cout << ans1 << " " << ans2;
    return 0;
}