最大流(flow)

最大流

简要题意:

自来水厂(源点)要把水送到 n n n户人家,他们之间的关系构成了一个图,图上还有一个污水处理厂(汇点
保证每户人家都有一条从源点到汇点的路径经过
图上每条水管都有一定的容量
请问自来水厂怎样才能使输水效率最高?

先了解几个定义:

流网络

流网络 G = ( V , E ) G=(V, E) G=(V,E)是一个有向图,其中每条边 ( u , v ) (u,v) (u,v)均有一非负容量 c ( u , v ) ≥ 0 c(u, v)≥0 c(u,v)0
流网络中有两个特殊的顶点: 源点 s s s和汇点 t t t
假定每个顶点都处于从源点到汇点的某条路径上,就是说,对每个顶点 v v v,存在一条路径 s → v → t s \to v\to t svt
G为连通图,且 ∣ E ∣ ≥ ∣ V ∣ − 1 |E| ≥|V|-1 EV1
边的流是一个实值函数f,满足下列三个性质:
1、容量限制:对所有 u , v ∈ V u,v\in V u,vV ,要求 f ( u , v ) ≤ c ( u , v ) f(u,v) ≤ c(u,v) f(u,v)c(u,v)
理解:流量不会超过边的容量
2、反对称性:对所有 u , v ∈ V u,v \in V u,vV, 要求 f ( u , v ) = − f ( v , u ) f(u,v) = -f(v, u) f(u,v)=f(v,u)
理解:一个方向的流是其反方向流的相反数
3、流守恒性:对所有 u ∈ V s , t u \in V_{s, t} uVs,t,要求 ∑ v f ( u , v ) − ∑ w f w , u = 0 \sum_vf(u,v) - \sum_wf_{w , u} = 0 vf(u,v)wfw,u=0
理解:进入点u的总流量=离开点u的总流量

残留网络

边的残留容量: r ( u , v ) = c ( u , v ) − f ( u , v ) r(u , v) = c(u , v) - f(u , v) r(u,v)=c(u,v)f(u,v)
残留网络:给定一流网络 G = ( V , E ) G=(V,E) G=(V,E)和流 f f f,由 f f f导出的 G G G的残留网络是 G f = ( v , E f ) G_f = (v , E_f) Gf=(v,Ef)
其中
E f = ( u , v ) ∈ V ∗ V : r ( u , v ) > 0 E_f = {(u , v) \in V*V:r(u , v) > 0} Ef=(u,v)VV:r(u,v)>0
这就是说,在残留网络中,每条边(称为残留边)能够容纳一个严格为正的网络流

0 < f ( u , v ) < c ( u , v ) 0<f(u,v)<c(u,v) 0<f(u,v)<c(u,v),如果 r ( u , v ) = c ( u , v ) − f ( u , v ) r(u , v) = c(u , v) - f(u , v) r(u,v)=c(u,v)f(u,v)
此时边(u,v)在残留网络中。

朴素做法(Ford-Fulkerson算法):

做法

每次从源点开始找到一条路径(边的容量不为0)到汇点,更新这条路径的剩余 r ( u , v ) r(u , v) r(u,v)
1、将 C ( u , v ) − a u g C(u , v) - aug C(u,v)aug
2、 C ( v , u ) + a u g C(v , u) + aug C(v,u)+aug 给程序一次反悔的机会
如果找不到就结束。

局限分析

如图所示的流网络,M非常大,例如10^10。在寻找增广路时不幸按红色所示线路,那么就会不断便利边 1 → 2 1\to 2 12,导致时间加大很多,所以我们要想办法优化这种情况,我们加一个距离标号就好了

距离标号

数组 d i d_i di i i i点到汇点的距离, v d i vd_i vdi表示 d 中为 i 的个数 d中为i的个数 d中为i的个数
每次 d f s dfs dfs便利时只能走 d x = d t o + 1 d_x = d_to + 1 dx=dto+1的点,如果没有找到点符合条件,那么 d x = min ⁡ 与 x 相连的点 d_x = \min与x相连的点 dx=minx相连的点
如果 v d vd vd中的某个数为 0 0 0则退出

code

版题

#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n , vd[N] , d[N] , s , t , m , u , v;
long long mp[N][N] , wi;
long long dfs(int x , long long past) {
    if(x == t) 
        return past;
    long long del = 0 , now = past;
    int mind = n - 1;
    for(int i = 1 ; i <= n ; i++) {
        if(!mp[x][i])
            continue;
        if(d[x] == d[i] + 1) {
            del = min(mp[x][i] , now);
            del = dfs(i , del);
            now -= del;
            mp[x][i] -= del;
            mp[i][x] += del;
            if(d[s] >= n) 
                return past - now;
            if(now == 0)
                break;
        }
        mind = min(mind , d[i]);
    }
    if(now == past) {
        vd[d[x]] --;
        if(!vd[d[x]])
            d[s] = n;
        d[x] = mind + 1;
        vd[d[x]] ++;
    }
    return past - now;
}
void flow() {
    long long ans = 0;
    vd[0] = n;
    while(d[s] < n) {
        ans += dfs(s , 100000000000000000);
    }
    printf("%lld" , ans);
}
int main() {
    scanf("%d%d%d%d" , &n , &m , &s , &t);
    for(int i = 1 ; i <= m ; i++) {
        scanf("%d%d%d" , &u , &v , &wi);
        mp[u][v] += wi;
    }
    flow();
    return 0;
}
posted @ 2023-03-25 16:13  2020fengziyang  阅读(5)  评论(0编辑  收藏  举报  来源