网络流(最大流)基础入门
好不容易大概搞懂了網(wǎng)絡(luò)流,寫個(gè)博客鞏固一下(盜了點(diǎn)圖,請圖主原諒)
**
定義 網(wǎng)絡(luò)流與最大流
**
網(wǎng)絡(luò)流是指給定一個(gè)有向圖,和兩個(gè)點(diǎn)–源點(diǎn)S和匯點(diǎn)T,點(diǎn)之間有連邊,
每條邊有一個(gè)容量限制,可以看作水管,網(wǎng)絡(luò)流就是指由S點(diǎn)流到T點(diǎn)的一個(gè)可行流。
最大流就是指所有可行流里面最大的流。
通俗的講,就是由若干個(gè)運(yùn)貨點(diǎn),一個(gè)是起點(diǎn),一個(gè)是終點(diǎn),有一些運(yùn)貨點(diǎn)由路相連,每條路有容量限制,走過那條路時(shí)運(yùn)送的貨物不能超過其中的容量限制,求最大流就是求從起點(diǎn)運(yùn)送盡量多的貨物到終點(diǎn),到達(dá)終點(diǎn)的貨物個(gè)數(shù)。
舉個(gè)例子:
此時(shí)最大流為4。
同時(shí),網(wǎng)絡(luò)流也有三個(gè)性質(zhì):
1、容量限制: f[u,v]<=c[u,v]
2、反對稱性:f[u,v] = - f[v,u]
3、流量平衡: 對于不是源點(diǎn)也不是匯點(diǎn)的任意結(jié)點(diǎn),流入該結(jié)點(diǎn)的流量和等于流出該結(jié)點(diǎn)的流量和。
網(wǎng)絡(luò)流必須滿足這三個(gè)性質(zhì)
第一個(gè)很容易理解,就是運(yùn)送的貨物不能夠超出限制
第二個(gè)也很容易理解,v向u流入x,v減少,u增加,如果從反方向看,就意味著
u減少-x,v增加-x,也就是u向v流入-x
第三個(gè)就是除了源點(diǎn)和匯點(diǎn)的運(yùn)貨點(diǎn)不能夠囤貨,收到貨物就要發(fā)出去。
最大流算法Dinic
最大流的算法有很多,有EK,Dinic,ISAP等
現(xiàn)在就介紹一種時(shí)間效率很高的算法–Dinic,
概念
其實(shí)EK,Dinic都有一個(gè)統(tǒng)一的思路–找增廣路,
相信同學(xué)們在學(xué)二分圖的時(shí)候已經(jīng)聽過這個(gè)名詞,
增廣路的意思是在當(dāng)前網(wǎng)絡(luò)之后找到一條能夠從源點(diǎn)到匯點(diǎn)能運(yùn)更多貨物的路徑。當(dāng)一條邊被增廣之后(即它是增廣路的一部分,或者說增廣路通過這條邊),這條邊還能通過的流量,叫做剩余流量,修改之后的圖稱為殘量網(wǎng)絡(luò)。
拿剛才的作為例子:
所以這類算法的思路就是,一直找增廣路,找到?jīng)]有為止
但是如果走完增廣路,有更優(yōu)的流法怎么辦?例如說:
又圖可知,最大流為4,
但在這個(gè)圖里面,若算法選擇了紅色路徑為增廣路,
此時(shí)流為2,這樣走之后就找不到增廣路了,因此最大流為2,顯然不對。
正確路徑應(yīng)是:
所以我們不能夠單純的尋找,需要給程序提供一個(gè)反悔的機(jī)會,
在這里我們引入一個(gè)反向弧的概念,也就是在增廣的同時(shí),給增廣路上的邊的反向邊容量增加所增廣流量:
意思是,程序可以反悔,把所流的流量流回去。
按照這個(gè)步驟,繼續(xù)執(zhí)行下去,程序就會找到另一條增廣路,從而得到正確答案。
將原本紅色的增廣流量流回去到1點(diǎn),然后換一條路徑到-2-T,此時(shí)藍(lán)色就可以走4-T增廣,其實(shí)是一種“偷梁換柱”的手法。
引入反向弧后,就能解決這個(gè)問題。
算法步驟
Dinic算法是在找增廣路 前提下加了幾個(gè)優(yōu)化,
先給一個(gè)步驟,后面詳細(xì)解釋:
1.用BFS建立分層圖
2.用DFS的方法尋找一條由源點(diǎn)到匯點(diǎn)的路徑,獲得這條路徑的流量x.
根據(jù)這條路徑修改整個(gè)圖,將所經(jīng)之處正向邊流量減少x,反向邊流量增加x
重復(fù)步驟2,直到DFS找不到新的路徑時(shí),重復(fù)步驟1
1、BFS
為了避免走重復(fù)的路徑,
首先從S點(diǎn)BFS,將整個(gè)圖分成若干層,變成層次圖,S點(diǎn)為第0層,
定義dis[u]為u所在的層數(shù),也就是至源點(diǎn)的最短距離。
(tips:如果u點(diǎn)到下一個(gè)節(jié)點(diǎn)v的邊剩余容量為0,則算法忽略此邊,因?yàn)槭S嗳萘繛?已經(jīng)無法增廣)
例如說:
如果在BFS的時(shí)候沒有遍歷過T點(diǎn),則說明源點(diǎn)與匯點(diǎn)不連通,函數(shù)返回0,算法結(jié)束。
有的話,返回1,代表建立成功,那么dis數(shù)組可以為接下來的DFS尋找增廣路提供優(yōu)化。
2、DFS
在BFS之后就要從源點(diǎn)開始DFS找增廣路,
DFS(u,exp)表示經(jīng)過u點(diǎn)時(shí)當(dāng)前流量為exp。
剛開始時(shí)從源點(diǎn)DFS時(shí),exp=∞,表示從該點(diǎn)流出的流量可以為無限大。
在DFS中遍歷u所連接的點(diǎn)v,dis[v]必須等于dis[u]+1,否則會多走沒有必要的路徑。
可以利用DFS進(jìn)行多路增廣來保證時(shí)間效率,即一次增廣多條路。
因邊有流量限制,所以DFS(u,exp)
[Math Processing Error]
當(dāng)u點(diǎn)為T時(shí),就等于找到一條由S點(diǎn)到T點(diǎn)的增廣路,返回exp至上一層。
當(dāng)u點(diǎn)不為T時(shí),定義一個(gè)值flow為在u點(diǎn)所增廣的流量,初始值為0
同時(shí)要找下一個(gè)節(jié)點(diǎn)以DFS(節(jié)點(diǎn)是一條以u為起點(diǎn)的邊上的終點(diǎn)),
但需要一些限制,首先dis[v]=dis[u]+1,而且該邊的剩余容量不能為0,否則肯定不能夠增廣更多的流量。
對于每次節(jié)點(diǎn)的DFS,如果該DFS可以增廣的流量為0,說明增廣失敗,就直接跳過后面,找下一個(gè)節(jié)點(diǎn)v。
不為0的話,當(dāng)前exp就減去增廣路的流量,代表著在u點(diǎn)還可以流的流量,如果exp已經(jīng)等于0就代表著在該點(diǎn)已經(jīng)沒有更多流量可以流過去了,必須退出增廣。
flow加上該增廣路增廣流量。
同時(shí),正向邊減去流量,代表剩余流量,反向邊加上流量(反向弧)
最后,該點(diǎn)所有的節(jié)點(diǎn)遍歷完后,返回該點(diǎn)所增廣的流量flow給上一層。
整個(gè)DFS結(jié)束后,最大流累加器ans加上S點(diǎn)的增廣流量,因?yàn)橥V沽薉FS,
所以已經(jīng)沒有路可以增廣,所以重新BFS給當(dāng)前殘余網(wǎng)絡(luò)建層次圖。
如果BFS返回0,即S點(diǎn)和T點(diǎn)不聯(lián)通時(shí),直接退出,輸出當(dāng)前ans,
也就是最大流
即偽代碼:
while(BFS()):
ans += DFS(S,inf)
這樣,Dinic算法的步驟就結(jié)束了。
模擬
讓我們來模擬一下吧:
以下面這個(gè)圖作為例子:
首先用BFS分層,層次圖如下:
分層后開始由S點(diǎn)DFS,
首先DFS(S,∞):
找到下一個(gè)節(jié)點(diǎn)3,dis[3]=dis[S]+1,所以可以,
此時(shí)流進(jìn)3的最大流量只能是現(xiàn)在走過路徑所允許的容量和當(dāng)前點(diǎn)點(diǎn)流量的最小值,即min(∞,5)=5,即進(jìn)入DFS(3,5)。
DFS(3,5):
找到下一個(gè)節(jié)點(diǎn)T,此時(shí)流進(jìn)T最大流量為min(5,3)=3。
所以進(jìn)入DFS(T,3)
DFS(T,3):
因?yàn)樽叩絋,所以找到一條增廣路,流量為3,返回增廣流量3上一層。
DFS(3,5):
找到一條增廣路,所以當(dāng)前點(diǎn)3還可以流的流量為:5-3=2(留了一些到T點(diǎn)),在該點(diǎn)所增廣得到的流量為0+3=3
正向邊減3,反向弧加3,也就是:
然后從3尋找下一個(gè)節(jié)點(diǎn)1,由于dis[1]!=dis[3]+1,所以跳過。
接下來沒有其他節(jié)點(diǎn)了,返回該點(diǎn)所增廣流量3到上一層。
DFS(S,∞):
在節(jié)點(diǎn)3得到增廣流量3,所以當(dāng)前節(jié)點(diǎn)可流流量,∞-3=∞(S點(diǎn)可流無窮的水),在S點(diǎn)增廣得到的流量為:0+3=3
同理,正向邊減3,反向弧加3,也就是:
修改完邊之后,遍歷S下一個(gè)節(jié)點(diǎn)1,因?yàn)閐is[1]=dis[S]+1,所以沒有問題,能流進(jìn)1最大流量:min(∞,5)=5,所以進(jìn)入DFS(1,5)
DFS(1,5):
找到下一個(gè)節(jié)點(diǎn)2,dis[2]=dis[1]+1,
能流進(jìn)2最大流量:min(5,6)=5,所以DFS(2,5)
DFS(2,5):
找到下一個(gè)節(jié)點(diǎn)T,dis[T]=dis[2]+1,
能流進(jìn)T最大流量:min(5,10)=5,所以DFS(T,5)
DFS(T,5):
走到T了,直接返回增廣流量5給上一層。
DFS(2,5):
flow=0+5=5,該點(diǎn)還可以流的流量為5-5=0,
正向邊減5,反向邊加5,也就是:
返回5到上一層
DFS(1,5):
flow=0+5=5,還可流流量5-5=0,正向邊減5,反向邊加5,
返回5到上一層
DFS(S,∞):
flow=5+3=8,還可流流量∞,正向邊減5,反向邊加5,
因?yàn)槭窃袋c(diǎn),且此時(shí)已經(jīng)找不到下一個(gè)節(jié)點(diǎn),所以不用回溯,累加器ans加上8。
因?yàn)檎也坏较乱粭l增廣路,所以重新BFS分層,分層后如圖所示:
(剩余容量為0的邊忽略,因?yàn)槠洳荒芡ㄟ^任何流量)
到這里請某位模擬大神上來模擬一下。
模擬后,你們對算法的過程應(yīng)該會有更深刻的了解。
反向邊的存儲和查詢
那么,在實(shí)際編程中,有什么要注意的?
首先,網(wǎng)絡(luò)流的邊可以用鏈?zhǔn)角跋蛐谴鎯?#xff0c;你們應(yīng)該知道,就是:
struct Edge{
int v,w,nxt;
}g[MAXM];
int head[MAXN],cnt;
void addEdge(int u,int v,int w){
g[cnt]=(Edge){v,w,head[u]};
head[u]=cnt;
++cnt;
}
然而反向弧應(yīng)該怎么存儲呢,在dfs過程中addEdge新建邊顯然是不現(xiàn)實(shí)的,
所以可以在建圖過程中加了普通邊后,把反向邊addEdge,容量為0,這樣會方便加。
例如:
addEdge(u,v,w);addEdge(v,u,0);
那么如何求一條邊的反向邊呢,假設(shè)那條邊的編號為i,那么反向邊應(yīng)為i^1,
例如說u-v編號為3,其反向邊v-u編號為3^1=2,
v-u編號為2,其反向邊u-v編號為2^1=3。
但要注意的是邊的編號要從0開始,否則這種方法無效
模版題
只要理解之后,最大流其實(shí)很簡單,其實(shí)還有很多的優(yōu)化,
在這里就由于時(shí)間關(guān)系不講了,可以課后找我問。
同時(shí)也給你們推薦一道模版題 「網(wǎng)絡(luò)流模板」圖圖的最大流
可以拿來測你們模版的時(shí)間(打滿優(yōu)化才能A)
總結(jié)
以上是生活随笔為你收集整理的网络流(最大流)基础入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信正则表达式 iOS
- 下一篇: xshell修改字体大小