最大流筆記


最大流

基本概念

最大流問題(Maximum Flow Problem)是一種組合最優化問題,是網絡流的基礎。把問題抽象成一個有向圖,從源點到匯點的每一條邊都有一個最大容量,指這條邊可以流過的流量最大值。問題要求的就是從源點到匯點的最大流量。注意和最長路的區別在於,它的流量可以通過多個路徑流到匯點。 
在求解問題之前,我們先來認識一些概念; 
容量網絡:我們剛才說的有向圖G,即每條邊都有一個最大容量的網絡; 
弧:就是網絡中的邊; 
弧分四類:(設有一條弧流量為h(u,v),最大容量為c) 
1,飽和弧,即h(u,v)= c; 
2,非飽和弧,即h(u,v)< c; 
3,非零弧,即h(u,v)< 0; 
4,零弧,即h(u,v)= 0; 
流量:一條弧實際所流過的流量; 
可行流:滿足條件1和2的一個網絡流f: 
1,流量限制,即每條弧的流量不能超過最大容量; 
2,平衡條件限制,即每條弧流入的流量一定等於它流出的流量; 
(特別地,如果一個網絡上所有流量均為0,則稱之為零流) 
鏈:即從點u到u1,u1到u2,……,un到v的一條序列,其中每兩個相鄰的點都要連有一條弧,設L是網絡G中一條從源點S到匯點T的鏈,則約定S到T的方向為正方向,但鏈中的弧方向不一定要和正方向相同。我們稱鏈中和正方向相同的弧為正向弧,反之為反向弧; 
增廣路:設f是G中的一個可行流,設L是從S至T的一條鏈,若: 
L中所有正向弧均為非飽和弧,且所有反向弧均為非零弧, 
那么這條鏈就稱為f的一條增廣路,沿着增廣路改進的操作叫做增廣; 
殘留容量:即在當前的可行流中弧h(u,v)的最大容量c減去已流流量f(u,v)的殘余流量,記為su(u,v); 
殘余網絡:即殘余容量組成的網絡,設殘余網絡G’,則對於G中的每一條弧h(u,v),若它是一條非飽和弧,則在G’中有一條弧h’(u,v),其最大容量c’(u,v)=c(u,v)-h(u,v),若它是一條非零弧,則在G’中有一條弧h”(v,u),其容量為c(u,v)。

問題求解

解決最大流有很多種解法,如EK、Isap、FF等,不過最有名的(主要是我們神聖的陳老師講了的)就是dinic算法,我們今天也只介紹這一種,其他的有興趣的童鞋可以自行百度。

dinic

學習dinic之前,我們先引入一個概念:層次圖; 
層次圖就是分層圖,一個點到源點的最短距離就叫做它的“層次”,分層圖中“層次”相同的點為一層。建立分層圖有一個好,就是同一層的點不可能在同一條鏈中,這樣在找增廣路時可以省去很多重復計算。 
所以dinic的基本思路就是不斷地根據殘余網絡建立層次圖,然后在層次圖中dfs找增廣路,直到無法增廣為止,這樣運行速度就會快很多。下面就來看一下具體的解決精髓:反向邊

反向邊

我們為什么要反向邊呢?看下面的一個例子(源點是1,匯點是4): 
圖1 
我們可以很快的找到一個增廣路1-2-3-4,增加流量為1,得到流如下: 
圖2 
可這時我們發現,弧(1,2)和(3,4)都已經是飽和弧,再也找不到增廣路了,而最大流卻明顯不是1,是2,即同時走1-2-4和1-3-4。那么問題出在哪里了呢? 
問題就是我們沒有給算法“后悔”的機會,它找完一個增廣路后就沒有改變原來2-3-4而走2-4的操作。那么怎么解決呢?暴力回溯?等着TLE吧。。。 
所以dinic(其實所有最大流的算法都是)用了一個非常神奇的東東來解決這個問題:反向邊。 
反向邊,就是對於網絡G中的每條弧h(u,v)都加入一條反向弧h(v,u),初始最大容量為c(u,v)。 
而在每次增廣的時候,就將增廣路上每一段容量減少n,同時將反向弧容量增加n。 
繼續看剛才的例子,加入反向邊后,網絡流修改成如下: 
圖3 
那么此時再找,就可以找到1-3-2-4這條增廣路,最終最大流量為2。 
圖3 
可是這樣做為什么是對的呢?因為當我們第二次增廣走過反向邊3-2時就相當於把2-3這條正向邊所用的流量給“退流”了,即不走2-3-4而改走從2出發的其它路,即2-4,最終流量也是一樣的。(注意2-4是必定存在的,因為如果沒有2-4的話就不會有1-3-2-4這條增廣路)同時3-4的流量就變到了1-3-4這條路上,2-3這條邊正向反向流量均為1,就相互抵消了,相當於沒有流量。 
這就是反向邊的基本思想,它通過“退流”的方式,給了算法后悔的機會。下面直接上代碼:

代碼(POJ1273——模板題)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6 struct edge{
 7     int y,r,next,op;
 8 }a[401];
 9 int head[201],q[5001],level[201],ans=0,x,y,z,n,m,vs,vt,tot=0;
10 void add(int x,int y,int z){
11     a[++tot].y=y;
12     a[tot].r=z;
13     a[tot].next=head[x];
14     head[x]=tot;
15     a[tot].op=tot+1;
16     a[++tot].y=x;
17     a[tot].r=0;
18     a[tot].next=head[y];
19     head[y]=tot;
20     a[tot].op=tot-1;
21 }
22 bool bfs(){
23     int u,tmp,v,f=1,r=1;
24     memset(level,0,sizeof(level));
25     q[f]=vs;
26     level[vs]=1;
27     while(f<=r){
28         v=q[f];
29         tmp=head[v];
30         while(tmp!=-1){
31             u=a[tmp].y;
32             if(a[tmp].r&&!level[u]){
33                 level[u]=level[v]+1;
34                 q[++r]=u;
35                 if(u==vt)return true;
36             }
37             tmp=a[tmp].next;
38         }
39         f++;
40     }
41     return false;
42 }
43 int dfs(int v,int num){
44     int value,flow,tmp,u,ans=0;
45     if(v==vt||!num)return num;
46     tmp=head[v];
47     while(tmp!=-1){
48         u=a[tmp].y;
49         value=a[tmp].r;
50         if(level[u]==level[v]+1){
51             flow=dfs(u,min(value,num));
52             if(flow){
53                 a[tmp].r-=flow;
54                 a[a[tmp].op].r+=flow;
55                 ans+=flow;
56                 num-=flow;
57                 if(!num)break;
58             }
59         }
60         tmp=a[tmp].next;
61     }
62     return ans;
63 }
64 int main(){
65     scanf("%d%d",&n,&m);
66     vs=1;
67     vt=m;
68     memset(head,255,sizeof(head));
69     for(int i=1;i<=n;i++){
70         scanf("%d%d%d",&x,&y,&z);
71         add(x,y,z);
72     }
73     while(bfs()){
74         ans+=dfs(1,2147483647);
75     }
76     printf("%d",ans);
77     return 0;
78 }

時間復雜度

dinic算法算是所有解決最大流問題的算法中效率最高的一種,證明比較復雜,這里直接給出復雜度: 
最壞情況時間復雜度:$O(V^{2}E)$; 
期望時間復雜度:$O(min(V^{\frac{3}{2}},E^{\frac{1}{2}})*E)$;
最好時間復雜度(二分圖):$O(\sqrt{V}E)$; 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM