费用流

posted on 2025-05-06 13:26:01 | under | source

SPFA

首先明确基本原理,就是不断跑代价最小的增广路,一条边能走需要满足残余流量为正。

用 SPFA 实现,复杂度 \(O(knm)\)\(k\) 为最大流。

dijskutra 原始对偶算法

很高大上对吧,其实就是加上势能来兼容负权边。

考虑每次跑最短路前设计一个势能 \(h\),将边权改为 \(w+h_u-h_v\)

具体来说:初始设为 \(0\)(反正也不会走反边),然后每次跑完更新 \(h_u\gets h_u+dis_u\)\(dis_u\) 是最短路数组。

证明:

  • 原来有残余流量:\(dis_u+w\ge dis_v\),移项 \(dis_u-dis_v+w\ge 0\)
  • 原来没有但是增广其反边:那么在最短路上所以 \(dis_u-dis_v+w=0\),反边边权为其相反数也是 \(0\),满足条件。

注意是加上而不是直接赋值,毕竟我们是在原有势能得到的边权上跑最短路。

复杂度 \(O(km\log n)\)

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

#define pir pair<int, int>
const int N = 5e3 + 5, M = 5e4 + 5, INF = 1.5e9;
int n, m, u, v, c, f, tot = 1, head[N], S, T;
int dis[N], h[N], vis[N], pre[N];
struct edge{int u, v, nxt, cost, f;} e[M << 1];

inline void add(int u, int v, int cost, int f) {e[++tot] = {u, v, head[u], cost, f}, head[u] = tot;}
inline void span(int u, int v, int cost, int f) {add(u, v, cost, f), add(v, u, -cost, 0);}
inline pir mcmf(int S, int T){
    int mxf = 0, mic = 0;
    for(int i = 1; i <= n; ++i) vis[i] = h[i] = pre[i] = 0, dis[i] = INF;
    while(1){
        priority_queue<pir, vector<pir>, greater<pir> > q;
        q.push({dis[S] = 0, S});
        while(!q.empty()){
            int u = q.top().second; q.pop();
            if(vis[u]) continue; vis[u] = true;
            for(int i = head[u]; i; i = e[i].nxt){
                if(!e[i].f) continue;
                int v = e[i].v, w = e[i].cost + h[u] - h[v];
                if(dis[v] > dis[u] + w){
                    dis[v] = dis[u] + w, pre[v] = i;
                    q.push({dis[v], v});
                }
            }
        }
        if(dis[T] == INF) break;
        int cc = INF, cs = 0;
        for(int i = T; i != S; i = e[pre[i]].u) cc = min(cc, e[pre[i]].f), cs += e[pre[i]].cost;
        mxf += cc, mic += cc * cs;
        for(int i = T; i != S; i = e[pre[i]].u) e[pre[i]].f -= cc, e[pre[i] ^ 1].f += cc;
        for(int i = 1; i <= n; ++i) h[i] += dis[i], dis[i] = INF, vis[i] = 0; 
    }
    return {mxf, mic};
}
signed main(){
    cin >> n >> m >> S >> T;
    for(int i = 1; i <= m; ++i) scanf("%d%d%d%d", &u, &v, &f, &c), span(u, v, c, f);
    pir ans = mcmf(S, T);
    printf("%d %d\n", ans.first, ans.second);
    return 0;
}
posted @ 2026-01-14 17:57  Zwi  阅读(0)  评论(0)    收藏  举报