[洛谷 P3381] 最小费用最大流 | 模板 Bellman-Ford寻找增广路 入门

题目链接

题目描述

给出一个包含 n n n 个点和 m m m 条边的有向图(下面称其为网络) G = ( V , E ) G=(V,E) G=(V,E),该网络上所有点分别编号为 1 ∼ n 1 \sim n 1n,所有边分别编号为 1 ∼ m 1\sim m 1m,其中该网络的源点为 s s s,汇点为 t t t,网络上的每条边 ( u , v ) (u,v) (u,v) 都有一个流量限制 w ( u , v ) w(u,v) w(u,v)和单位流量的费用 c ( u , v ) c(u,v) c(u,v)

你需要给每条边 ( u , v ) (u,v) (u,v) 确定一个流量 f ( u , v ) f(u,v) f(u,v),要求:

  1. 0 ≤ f ( u , v ) ≤ w ( u , v ) 0 \leq f(u,v) \leq w(u,v) 0f(u,v)w(u,v) (每条边的流量不超过其流量限制);
  2. ∀ p ∈ { V ∖ { s , t } } \forall p \in \{V \setminus \{s,t\}\} p{V{s,t}} ∑ ( i , p ) ∈ E f ( i , p ) = ∑ ( p , i ) ∈ E f ( p , i ) \sum_{(i,p) \in E}f(i,p)=\sum_{(p,i)\in E}f(p,i) (i,p)Ef(i,p)=(p,i)Ef(p,i)(除了源点和汇点外,其他各点流入的流量和流出的流量相等);
  3. ∑ ( s , i ) ∈ E f ( s , i ) = ∑ ( i , t ) ∈ E f ( i , t ) \sum_{(s,i)\in E}f(s,i)=\sum_{(i,t)\in E}f(i,t) (s,i)Ef(s,i)=(i,t)Ef(i,t) (源点流出的流量等于汇点流入的流量)。

定义网络 G G G 的流量 F ( G ) = ∑ ( s , i ) ∈ E f ( s , i ) F(G)=\sum_{(s,i)\in E}f(s,i) F(G)=(s,i)Ef(s,i),网络 G G G 的费用 C ( G ) = ∑ ( i , j ) ∈ E f ( i , j ) × c ( i , j ) C(G)=\sum_{(i,j)\in E} f(i,j) \times c(i,j) C(G)=(i,j)Ef(i,j)×c(i,j)

你需要求出该网络的最小费用最大流,即在 F ( G ) F(G) F(G) 最大的前提下,使 C ( G ) C(G) C(G) 最小。

输入格式

输入第一行包含四个整数 n , m , s , t n,m,s,t n,m,s,t,分别代表该网络的点数 n n n,网络的边数 m m m,源点编号 s s s,汇点编号 t t t
接下来 m m m 行,每行四个整数 u i , v i , w i , c i u_i,v_i,w_i,c_i ui,vi,wi,ci,分别代表第 i i i 条边的起点,终点,流量限制,单位流量费用。

输出格式

输出两个整数,分别为该网络的最大流 F ( G ) F(G) F(G),以及在 F ( G ) F(G) F(G) 最大的前提下,该网络的最小费用 C ( G ) C(G) C(G)

输入输出样例

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5
50 280

说明/提示
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 5 × 1 0 4 1 \leq n \leq 5\times 10^4 1n5×104 1 ≤ m ≤ 5 × 1 0 4 1 \leq m \leq 5 \times 10^4 1m5×104 1 ≤ s 1 \leq s 1s , t ≤ n t \leq n tn 0 ≤ w i , c i ≤ 1 0 3 0 \leq w_i,c_i \leq 10^3 0wi,ci103 ,且该网络的最大流和最小费用 ≤ 2 31 − 1 \leq 2^{31} - 1 2311
输入数据随机生成。

思路:
EK算法类似,但每次用 B e l l m a n − F o r d Bellman-Ford BellmanFord 算法而非 B F S BFS BFS(其实差不多)找增广路。
只要初始流是该流量下的最小费用可行流,每次增广之后的新流都是新流量下的最小费用流。
费用值可正可负

typedef pair<ll, ll> PLL;
struct Edge {
    int u, v, cap, flow, cost;
    Edge(int _u, int _v, int _cap, int _flow, int _cost) {
        u = _u, v = _v, cap = _cap, flow = _flow, cost = _cost;
    }
};
struct MinCostMaxFlow {
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    int vis[maxn], dis[maxn], p[maxn], a[maxn];
    void init(int x) {
        this->n = x;
        for (int i = 0; i <= x; i++) G[i].clear();
        edges.clear();
    }
    void add(int u, int v, int cap, int cost) {
        edges.push_back(Edge(u, v, cap, 0, cost));
        edges.push_back(Edge(v, u, 0, 0, -cost));
        m = edges.size();
        G[u].push_back(m - 2);
        G[v].push_back(m - 1);
    }
    bool BellmanFord(int s, int t, ll &flow, ll &cost) {
        for (int i = 0; i <= n; i++) dis[i] = 0x3f3f3f3f;
        memset(vis, 0, sizeof vis);
        queue<int> que;
        que.push(s);
        dis[s] = 0, vis[s] = 0, p[s] = 0, a[s] = 0x3f3f3f3f;
        while (que.size()) {
            int u = que.front();
            que.pop();
            vis[u] = 0; /// not in the queue
            for (int i = 0; i < G[u].size(); i++) {
                int id = G[u][i];
                Edge e = edges[id];
                int to = e.v;
                if (e.cap > e.flow && dis[to] > dis[u] + e.cost) {
                    dis[to] = dis[u] + e.cost;
                    p[to]   = id;
                    a[to]   = min(a[u], e.cap - e.flow);
                    if (!vis[to]) {
                        que.push(to);
                        vis[to] = 1;
                    }
                }
            }
        }
        if (dis[t] >= 0x3f3f3f3f) return false;
        flow += a[t];
        cost += 1LL * dis[t] * 1LL * a[t];
        for (int u = t; u != s; u = edges[p[u]].u) {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }
    PLL MinCostAndMaxFlow(int s, int t) {
        ll flow = 0;
        ll cost = 0;
        while (BellmanFord(s, t, flow, cost));
        return {flow, cost};
    }
} solve;
int n, m, s, t;
int main() {
    cin >> n >> m >> s >> t;
    solve.init(n);
    for (int i = 1; i <= m; i++) {
        int u = read, v = read, flow = read, cost = read;
        solve.add(u, v, flow, cost);
    }
    PLL ans = solve.MinCostAndMaxFlow(s, t);
    cout << ans.first << ' ' << ans.second << endl;
    return 0;
}
posted @ 2021-08-31 23:11  PushyTao  阅读(32)  评论(0编辑  收藏  举报