网络流——最大流
一、什么是網(wǎng)絡(luò)流
網(wǎng)絡(luò)流是指給定一個(gè)有向圖,其中有兩個(gè)特殊的點(diǎn):源點(diǎn) sss(Source)和匯點(diǎn) ttt(Sink);每條邊都有一個(gè)指定的流量上限,下文均稱之為容量(Capacity),即經(jīng)過(guò)這條邊的流量不能超過(guò)容量,這樣的圖被稱為網(wǎng)絡(luò)流圖。同時(shí),除了源點(diǎn)和匯點(diǎn)外,所有點(diǎn)的入流和出流都相等,源點(diǎn)只有流出的流,匯點(diǎn)只有流入的流,網(wǎng)絡(luò)流就是從 sss 到 ttt 的一個(gè)可行流。
二、可行流、最大流
定義 c(u,v)c(u,v)c(u,v) 表示邊 (u,v)(u,v)(u,v) 的容量,f(u,v)f(u,v)f(u,v) 表示邊 (u,v)(u,v)(u,v) 的流量。如果滿足 0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v),則稱f(u,v)f(u,v)f(u,v) 為邊 (u,v)(u,v)(u,v) 上的流量。
如果有一組流量滿足:源點(diǎn) sss 的流出量等于整個(gè)網(wǎng)絡(luò)的流量,匯點(diǎn) ttt 的流入量等于整個(gè)網(wǎng)絡(luò)的流量,除了任意一個(gè)不是 sss 或 ttt 的點(diǎn)的總流入量等于總流出量。那么整個(gè)網(wǎng)絡(luò)中的流量被稱為一個(gè)可行流。
在所有可行流中,最大流指其中流量最大的一個(gè)流的流量。
三、相關(guān)定義
四、網(wǎng)絡(luò)流的性質(zhì)
1.容量限制
對(duì)于任何一條邊,都有 0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v)0≤f(u,v)≤c(u,v)。
2.斜對(duì)稱性
對(duì)于任何一條邊,都有 f(u,v)=?f(v,u)f(u,v)=?f(v,u)f(u,v)=?f(v,u)。即從 uuu 到 vvv 的流量一定等于從 vvv 到 uuu 的流量的相反數(shù)。
3.流守恒性
對(duì)于任何一個(gè)點(diǎn) uuu,如果滿足 u≠su≠su?=s 并且 u≠tu≠tu?=t,那么一定有 ∑f(u,v)=0∑f(u,v)=0∑f(u,v)=0,即 uuu 到相鄰節(jié)點(diǎn)的流量之和為 000。因?yàn)?uuu 本身不會(huì)制造和消耗流量。
五、最大流的求解
增廣路思想
不防依照這個(gè)思想先模擬一遍:
如果我們把每條邊的的信息用殘量 / 容量表示出來(lái),可以得到下圖:
假設(shè)我們第一次找到的増廣路為1?2?3?41?2?3?41?2?3?4,那么我們把這條路徑上的邊的 w(u,v)w(u,v)w(u,v) 減去 minf(u,v)min{f(u,v)}minf(u,v) 即 111,得到下圖:
然后我們發(fā)現(xiàn)已經(jīng)沒有増廣路了,此時(shí)算出來(lái)的“最大流”為 1。但是我們可以手動(dòng)計(jì)算一下,這張圖的最大流其實(shí)是 2。這個(gè)最大流的路徑為 1?2?41?2?41?2?4(流量為 111)和 1?3?41?3?41?3?4(流量為 111)。
因此,我們可以發(fā)現(xiàn)這樣的過(guò)程是錯(cuò)誤的。原因就是増廣路在一定意義上是有順序的,說(shuō)白了就是沒有給它反悔的機(jī)會(huì)。所以接下來(lái)我們要引入反向邊的概念。
反向邊思想
通過(guò)上文的分析我們已經(jīng)知道,當(dāng)我們?cè)趯ふ覊垙V路的時(shí)候,找到的并不一定是最優(yōu)解。如果我們對(duì)正向邊的 w(u,v)w(u,v)w(u,v) 減去 flowflowflow 的同時(shí),將對(duì)應(yīng)的反向邊的 w(v,u)w(v,u)w(v,u) 加上 flowflowflow,我們就相當(dāng)于可以反悔從這條邊流過(guò)。
那么我們可以建立反向邊,初始時(shí)每條邊的 w(u,v)=c(u,v)w(u,v)=c(u,v)w(u,v)=c(u,v),它的反向邊的 w(v,u)=0w(v,u)=0w(v,u)=0(顯然反向邊不能有流量,因此殘量為 000)。
接下來(lái)再看一下上面那個(gè)例子,我們只用 w(u,v)w(u,v)w(u,v) 來(lái)表示每條邊(包括反向邊)的信息:
接下來(lái)開始尋找増廣路,假如還是 1?2?3?41?2?3?41?2?3?4 這條路徑。
我們需要把 w(1,2)w(1,2)w(1,2),w(2,3)w(2,3)w(2,3),w(3,4)w(3,4)w(3,4) 減少 111,同時(shí)把反向邊的 w(2,1)w(2,1)w(2,1),w(3,2)w(3,2)w(3,2),w(4,3)w(4,3)w(4,3) 增加 1。那么可以得到下圖:
繼續(xù)從 sss 開始尋找増廣路(不需要考慮邊的類型),顯然可以發(fā)現(xiàn)路徑 1?3?2?41?3?2?41?3?2?4,其中 flow=1flow=1flow=1。更新邊的信息,得到下圖:
此時(shí)我們發(fā)現(xiàn)沒有増廣路了,為了直觀觀察這個(gè)網(wǎng)絡(luò),我們?nèi)サ舴聪蜻?#xff0c;顯然我們求出的最大流為 2
正確性
當(dāng)我們第二次増廣邊 (2,3)(2,3)(2,3) 走這條反向邊 (3,2)(3,2)(3,2) 時(shí),把 (2,3)(2,3)(2,3) 和 (3,2)(3,2)(3,2) 的流量抵消了,相當(dāng)于把 (2,3)(2,3)(2,3) 這條正向邊的流量給退了回去使得可以不走 (2,3)(2,3)(2,3) 這條邊。
如果反向邊 (v,u)(v,u)(v,u) 的流量不能完全抵消正向邊 (u,v)(u,v)(u,v),那么意味著從 uuu 開始還可以流一部分流量到 vvv,這樣也是允許的。
思路總結(jié)
六、算法實(shí)現(xiàn)
最大流算法主要有兩類,增廣路算法和預(yù)留推進(jìn)算法,下面介紹的兩種算法都屬于增廣路算法。
增廣路算法都是基于增廣路定理(Augmenting Path Theorem):網(wǎng)絡(luò)達(dá)到最大流當(dāng)且僅當(dāng)殘留網(wǎng)絡(luò)中沒有増廣路。
- Ford-Fulkerson(FF算法)
思路:增廣路思想的完全模擬,它通過(guò)深度優(yōu)先搜索來(lái)尋找增廣路,并沿著它增廣,直到找不到增廣路為止。
老規(guī)矩,上代碼之前先來(lái)道模板題:【模板】網(wǎng)絡(luò)最大流
c++代碼:
時(shí)間復(fù)雜度:記最大流的流量為FFF,那么Fork?fulkersonFork-fulkersonFork?fulkerson算法最多進(jìn)行FFF次深度優(yōu)先搜索,所以其復(fù)雜度為O(F∣E∣)O(F|E|)O(F∣E∣)。不過(guò),這是一個(gè)很松的上界,達(dá)到這種最壞復(fù)雜度的情況幾乎不存在。所以在多數(shù)情況下,即便通過(guò)估算得到的復(fù)雜度偏高,實(shí)際運(yùn)用當(dāng)中也還是比較快的。
- Dinic
思路:與FFFFFF算法相對(duì),DinicDinicDinic算法總是尋找最短的增廣路,并沿著它增廣。因?yàn)樽疃淘鰪V路的長(zhǎng)度在增廣過(guò)程中始終不會(huì)變短,所以無(wú)需每次都通過(guò)深度預(yù)先搜索來(lái)尋找最短增廣路,我們可以先進(jìn)行一次寬度優(yōu)先搜索,然后考慮由近距離頂點(diǎn)指向遠(yuǎn)距離頂點(diǎn)的邊所組成的分層圖,在上面進(jìn)行深度優(yōu)先搜索尋找最短增廣路。如果在分層圖上找不到新的增廣路了,則說(shuō)明最短增廣路的長(zhǎng)度確實(shí)變長(zhǎng)了,或不存在增廣路了,于是重新通過(guò)寬度優(yōu)先搜索構(gòu)造新的分層圖。此外,還可以對(duì)這個(gè)算法進(jìn)行優(yōu)化。
當(dāng)前弧優(yōu)化:在每次對(duì)分層圖進(jìn)行深度優(yōu)先搜索尋找增廣路時(shí),避免對(duì)一條沒有用的邊進(jìn)行多次檢查。
c++代碼:
時(shí)間復(fù)雜度:每一步構(gòu)造分層圖的復(fù)雜度為O(∣E∣)O(|E|)O(∣E∣),加入當(dāng)前弧優(yōu)化后,可以保證對(duì)每次分層圖進(jìn)行深度優(yōu)先搜索復(fù)雜度為O(∣E∣∣V∣)O(|E||V|)O(∣E∣∣V∣),而每一步完成之后最短增廣路的長(zhǎng)度都會(huì)至少增加1,由于增廣路的長(zhǎng)度不會(huì)超過(guò)∣V∣?1|V|-1∣V∣?1,因此最多重復(fù)O(|V|)步就可以了,這樣總的復(fù)雜度就是O(∣E∣∣V∣O(|E||V|O(∣E∣∣V∣2)。不過(guò),該算法在實(shí)際應(yīng)用中速度非常快,很多時(shí)候即便圖的規(guī)模比較大也沒有問題。
- 兩種算法的對(duì)比
評(píng)測(cè)記錄:
第一個(gè)是Dinic算法,耗時(shí)470ms,第二個(gè)是FF算法,耗時(shí)1220ms。
總結(jié):99%的網(wǎng)絡(luò)流算法,都可以用Dinic去解。卡Dinic的毒瘤出題人,都是*嗶*
不好意思爆粗口了,不過(guò),還真的有這種毒瘤出題人💢,👇👇👇👇👇
【模板】最大流 加強(qiáng)版 / 預(yù)流推進(jìn)
解決這道題就必須要用我上面說(shuō)的第二種最大流算法:預(yù)流推進(jìn)法。
但是我太懶(蒻)了,現(xiàn)在并不想去寫那種算法,就交給你們了QWQ
七.最大流的應(yīng)用
洛谷P1345奶牛的電信
做這道題之前,先說(shuō)一個(gè)很重要的定理:
最大流最小割定理:最大流等于最小割。
那么,最小割又是什么呢?最小割是要求為了使原點(diǎn)(記為S)和匯點(diǎn)(記為T)不連通,最少要割幾條邊。
好了,你是不是覺得你可以去做這道題了(其實(shí)說(shuō)的就是我 ),裸的割邊,打個(gè)Dinic就行了,但有時(shí)候我們還是太naive了。
重新讀題,會(huì)發(fā)現(xiàn)這題割的不是邊,是點(diǎn)。所以說(shuō),我們需要一個(gè)割邊轉(zhuǎn)割點(diǎn)的小技巧。
我們可以考慮“拆點(diǎn)”,即把一個(gè)點(diǎn)拆成兩個(gè)點(diǎn),中間連一條邊權(quán)為1的邊。
前一個(gè)點(diǎn)作為“入點(diǎn)”,別的點(diǎn)連邊連入這里。
后一個(gè)點(diǎn)作為“出點(diǎn)”,出去的邊從這里出去。
這樣,只要我們切斷中間那條邊,就可以等效于除去這個(gè)點(diǎn),如圖:
紅色的邊邊權(quán)為1,黑色的邊邊權(quán)為infinfinf。
原點(diǎn)和匯點(diǎn)的內(nèi)部邊權(quán)為infinfinf,因?yàn)轱@然這兩個(gè)點(diǎn)不能刪除。
題面給的邊刪除沒意義(因?yàn)槲覀円獎(jiǎng)h點(diǎn)),所以也設(shè)為inf(事實(shí)上設(shè)為1也沒問題,因?yàn)閯h除這條邊的權(quán)值可以理解為刪除了一個(gè)點(diǎn))
至此,我們就可以把這道題AC了
c++代碼:
總結(jié)
- 上一篇: jquery 获取系统默认年份_你没有看
- 下一篇: cuda gpu相关汇总