Luogu P3376 模板 網路最大流

2022-04-08 05:42:30 字數 3815 閱讀 7013

傳送門

noip之前,我絕對不學網路流。

——我

網路流可以解決這樣一類問題:給定乙個有向圖,每條邊有乙個流量上限,求從源點到匯點所能運輸的最大流量。

解決這類問題的一種演算法叫做$dinic$。

一些性質

對於任意乙個時刻,設c(u,v)為容量,f(u,v)為實際流量,則整個圖g的流網路滿足3個性質:

原理

假設存在乙個有一些流但沒有達到最大流的網路,如果能找到一條路徑,使總流量增加,這樣的路徑被稱為增廣路

反過來說,如果圖中不存在增廣路,那麼這一定是網路最大流。

$dinic$演算法就是乙個不斷尋找增廣路的過程。

然而,這個結論可以很容易舉出反例:

這是這個網路中的流的一種方式。它已經沒有增廣路了,但是,顯然,它不是最優的。

那麼,假設程式已經選擇了這種方式,應該怎麼辦?這時候就需要引入反向邊的概念。

一條正常的邊流了多少,它的反向邊的容量就增加多少。

反向邊初始容量為0。

那麼,加上反向邊,就得到了這樣的一張圖:

在這張圖上再次尋找增廣路,可以得到

這樣就得到了最大流。它和這樣的流的方式是等價的

不難發現,反向邊的作用就在於使流錯了的邊再流回去。

實現

$dinic$演算法的流程可以概括為:

bfs檢查是否有增廣路。若沒有,則已達到最大流,程式結束。

dfs找到一條增廣路,將增加的流量計入答案。

返回1。

需要定義這些變數:

int dis; //

從源點到這一點的距離(經過的邊數)

int cur; //

走到這一點後,下乙個可能找到增廣路的邊

cur的意義將在$dfs$的部分詳細解釋。

$dinic()$

根據上述流程,可以得到這樣的**

void

dinic()

}

$bfs()$

想要找到增廣路,需要檢查每一條邊剩餘的容量w[i]能不能組成一條從s到t的通路。

在尋找的過程中,可以處理出最短路,即使找到了t,也不退出while迴圈。

和普通的$bfs$類似,首先將所有點到源點s的距離dis設定為-1,表示未訪問過。

源點s本身的dis設定為0,並將s壓入佇列q。

每次彈出隊首,檢查它所連的邊能到達的每乙個點v。

若v未被訪問過,且(u,v)還有剩餘的流量(dis[v] == -1 && w[i]),說明(u,v)是一條可行的道路,將v壓入佇列。

如果搜尋到了匯點t(dis[t]≠-1)

,說明有增廣路,返回true。否則返回false。

bool

bfs() }}

if(dis[t] == -1) return

false

;

return

true

;}

$dfs()$

dfs中每次傳遞兩個變數:當前點u,當前流flow。用sum記錄找到的增廣路的總量。

檢查u所連的邊能到達的每乙個點v。

for(int

&i = cur[u]; i != -1

; i = nxt[i])

注意,高亮部分和平時有一點不同,

可以每次使到達的點的cur[u]改為nxt[i]。

也就是說,如果我們已經試過了這條邊,那麼它在這次搜尋中一定不會再次成為增廣路了。

下一次經過這一點時,直接從它的下一條邊nxt[i]開始嘗試即可。

這樣可以比head→nxt→nxt→……節省一些時間。

在$dinic()$中,每次$bfs$後,將cur的初始值設定為head。

這個操作叫做當前弧優化。

如果v在這次bfs找到的最短路上,且(u,v)還有剩餘的流量(dis[v] == dis[u]+1 && w[i]),說明(u,v)是一條可行的道路。

遞迴$dfs$點v。如果邊(u,v)的剩餘流量w[i]小於當前流量flow,那麼最多就只能流w[i]而不是flow,

下傳的flow應該改為w[i],即dfs(v,min(flow,w[i]))。

如果找到匯點t,則說明找到了一條增廣路,當前的flow則為增廣的流量。回溯,

記這個返回值為ff。

u之前的點的剩餘容量都減小了ff,即flow -= f;總流量sum增加ff。

同時,當前邊的容量w[i]減小ff,反向邊的容量w[i^1]增加ff。

如果flow減ff等於0了,那麼就可以跳出for迴圈。

注意,在建邊時,如果從偶數開始,那麼i的反向邊就可以用i^1表示。

例如,第一條邊0,反向邊1;第二條邊2(10),反向邊3(11)。

當然,所有head的初值就要設定為-1;在for迴圈列舉邊時,邊界條件就為i != -1。

如果dfs結束時,sum的值為0,則說明沒有找到增廣路。那麼,這次搜尋中一定不會在以後找到一條經過u點的增廣路。為了防止再次找到u點,將dis[u]改為0。

最後返回sum。

int dfs(int u,int

flow)

}if(!sum) dis[u] = -1

;

return

sum;

}

是不是很短!

完整**如下

#include#include

#include

#include

#include

#define mogeko qwq

using

namespace

std;

const

int maxn = 2e5+10

;const

int inf = 0x3f3f3f3f

;int

n,m,s,t,x,y,z,ans,cnt;

inthead[maxn],to[maxn],nxt[maxn],w[maxn];

intcur[maxn],dis[maxn];

void add(int x,int y,int

z) bool

bfs() }}

if(dis[t] == -1) return

false

;

return

true;}

int dfs(int u,int

flow)

}if(!sum) dis[u] = -1

;

return

sum;

}void

dinic()

}int

main()

dinic();

printf("%d

",ans);

return0;

}

view code

luogu P3376 模板 網路最大流

如題,給出乙個網路圖,以及其源點和匯點,求出其網路最大流。輸入格式 第一行包含四個正整數n m s t,分別表示點的個數 有向邊的個數 源點序號 匯點序號。接下來m行每行包含三個正整數ui vi wi,表示第i條有向邊從ui出發,到達vi,邊權為wi 即該邊最大流量為wi 輸出格式 一行,包含乙個正...

Luogu P3376 模板 網路流 最大流

如題,給出乙個網路圖,以及其源點和匯點,求出其網路最大流。輸入格式 第一行包含四個正整數n m s t,分別表示點的個數 有向邊的個數 源點序號 匯點序號。接下來m行每行包含三個正整數ui vi wi,表示第i條有向邊從ui出發,到達vi,邊權為wi 即該邊最大流量為wi 輸出格式 一行,包含乙個正...

luogu3376 模板 網路最大流

題目描述 如題,給出乙個網路圖,以及其源點和匯點,求出其網路最大流。輸入輸出格式 輸入格式 第一行包含四個正整數n m s t,分別表示點的個數 有向邊的個數 源點序號 匯點序號。接下來m行每行包含三個正整數ui vi wi,表示第i條有向邊從ui出發,到達vi,邊權為wi 即該邊最大流量為wi 輸...