傳送門
noip之前,我絕對不學網路流。網路流可以解決這樣一類問題:給定乙個有向圖,每條邊有乙個流量上限,求從源點到匯點所能運輸的最大流量。——我
解決這類問題的一種演算法叫做$dinic$。
一些性質
對於任意乙個時刻,設c(u,v)為容量,f(u,v)為實際流量,則整個圖g的流網路滿足3個性質:
原理
假設存在乙個有一些流但沒有達到最大流的網路,如果能找到一條路徑,使總流量增加,這樣的路徑被稱為增廣路。
反過來說,如果圖中不存在增廣路,那麼這一定是網路最大流。
$dinic$演算法就是乙個不斷尋找增廣路的過程。
然而,這個結論可以很容易舉出反例:
這是這個網路中的流的一種方式。它已經沒有增廣路了,但是,顯然,它不是最優的。
那麼,假設程式已經選擇了這種方式,應該怎麼辦?這時候就需要引入反向邊的概念。
一條正常的邊流了多少,它的反向邊的容量就增加多少。
反向邊初始容量為0。
那麼,加上反向邊,就得到了這樣的一張圖:
在這張圖上再次尋找增廣路,可以得到
這樣就得到了最大流。它和這樣的流的方式是等價的
不難發現,反向邊的作用就在於使流錯了的邊再流回去。
實現
$dinic$演算法的流程可以概括為:
bfs檢查是否有增廣路。若沒有,則已達到最大流,程式結束。
dfs找到一條增廣路,將增加的流量計入答案。
返回1。
需要定義這些變數:
int dis; //cur的意義將在$dfs$的部分詳細解釋。從源點到這一點的距離(經過的邊數)
int cur; //
走到這一點後,下乙個可能找到增廣路的邊
$dinic()$
根據上述流程,可以得到這樣的**
void$bfs()$dinic()
}
想要找到增廣路,需要檢查每一條邊剩餘的容量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$dfs()$bfs() }}
if(dis[t] == -1) return
false
;
return
true
;}
dfs中每次傳遞兩個變數:當前點u,當前流flow。用sum記錄找到的增廣路的總量。
檢查u所連的邊能到達的每乙個點v。
for(int如果v在這次bfs找到的最短路上,且(u,v)還有剩餘的流量(dis[v] == dis[u]+1 && w[i]),說明(u,v)是一條可行的道路。&i = cur[u]; i != -1
; i = nxt[i])
注意,高亮部分和平時有一點不同,
可以每次使到達的點的cur[u]改為nxt[i]。
也就是說,如果我們已經試過了這條邊,那麼它在這次搜尋中一定不會再次成為增廣路了。
下一次經過這一點時,直接從它的下一條邊nxt[i]開始嘗試即可。
這樣可以比head→nxt→nxt→……節省一些時間。
在$dinic()$中,每次$bfs$後,將cur的初始值設定為head。
這個操作叫做當前弧優化。
遞迴$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表示。如果dfs結束時,sum的值為0,則說明沒有找到增廣路。那麼,這次搜尋中一定不會在以後找到一條經過u點的增廣路。為了防止再次找到u點,將dis[u]改為0。例如,第一條邊0,反向邊1;第二條邊2(10),反向邊3(11)。
當然,所有head的初值就要設定為-1;在for迴圈列舉邊時,邊界條件就為i != -1。
最後返回sum。
int dfs(int u,int是不是很短!flow)
}if(!sum) dis[u] = -1
;
return
sum;
}
完整**如下
#include#includeview code#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;
}
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 輸...