在閱讀本文前,建議先自學最大流的Ek算法。ios
Ek的核心是執行bfs,一旦找到增廣路就停下來進行增廣。換言之,執行一遍BFS執行一遍DFS,這使得效率大大下降。因而咱們能夠考慮優化。算法
在一次BFS中,找到的增廣路可能不止一條,這時咱們能夠本着「儘可能少進行BFS」的想法,在一次bfs後把全部能增廣的路徑所有增廣。
具體怎麼作呢?
仍然是:
while(bfs(源點,匯點)) dfs();數組
每次bfs標記出每一個點的「深度」,也就是距離源點的長度。咱們將獲得的新圖稱做分層圖。接下來咱們在分層圖上進行增廣,把能增廣的路徑所有增廣。
其它部分和Ek大致相同。優化
實際上是一個很強的優化spa
每次增廣一條路後能夠看作「榨乾」了這條路,既然榨乾了就沒有再增廣的可能了。但若是每次都掃描這些「枯萎的」邊是很浪費時間的。那咱們就記錄一下「榨取」到那條邊了,而後下一次直接從這條邊開始增廣,就能夠節省大量的時間。這就是當前弧優化code
具體怎麼實現呢,先把鏈式前向星的head數組複製一份,存進cur數組,而後在cur數組中每次記錄「榨取」到哪條邊了。blog
//by floatiy #include<iostream> #include<cstdio> #include<algorithm> #include<queue> using namespace std; const int MAXN = 10000 + 5; const int MAXM = 100000 + 5; const int INF = 1e9; int n,m; int s,t;//源點 匯點 int maxflow;//答案 struct Edge { int next; int to,flow; } l[MAXM << 1]; int head[MAXN],cnt = 1; int deep[MAXN],cur[MAXN];//deep記錄bfs分層圖每一個點到源點的距離 queue <int> q; inline void add(int x,int y,int z) { cnt++; l[cnt].next = head[x]; l[cnt].to = y; l[cnt].flow = z; head[x] = cnt; return; } int min(int x,int y) { return x < y ? x : y; } int dfs(int now,int t,int lim) {//分別是當前點,匯點,當前邊上最小的流量 if(!lim || now == t) return lim;//終止條件 // cout<<"DEBUG: DFS HAS BEEN RUN!"<<endl; int flow = 0; int f; for(int i = cur[now]; i; i = l[i].next) {//注意!當前弧優化 cur[now] = i;//記錄一下榨取到哪裏了 if(deep[l[i].to] == deep[now] + 1 //誰叫你是分層圖 && (f = dfs(l[i].to,t,min(lim,l[i].flow)))) {//若是還能找到增廣路 flow += f; lim -= f; l[i].flow -= f; l[i ^ 1].flow += f;//記得處理反向邊 if(!lim) break;//沒有殘量就意味着不存在增廣路 } } return flow; } bool bfs(int s,int t) { for(int i = 1; i <= n; i++) { cur[i] = head[i];//拷貝一份head,畢竟咱們還要用head deep[i] = 0x7f7f7f7f; } while(!q.empty()) q.pop();//清空隊列 其實沒有必要了 deep[s] = 0; q.push(s); while(!q.empty()) { int tmp = q.front(); q.pop(); for(int i = head[tmp]; i; i = l[i].next) { if(deep[l[i].to] > INF && l[i].flow) {//有流量就增廣 //deep我賦的初值是0x7f7f7f7f 大於 INF = 1e9) deep[l[i].to] = deep[tmp] + 1; q.push(l[i].to); } } } if(deep[t] < INF) return true; else return false; } void dinic(int s,int t) { while(bfs(s,t)) { maxflow += dfs(s,t,INF); // cout<<"DEBUG: BFS HAS BEEN RUN!"<<endl; } } int main() { cin>>n>>m;//點數邊數 cin>>s>>t; int x,y,z; for(int i = 1; i <= m; i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,0); } // cout<<"DEBUG: ADD FININSHED!"<<endl; dinic(s,t); printf("%d",maxflow); return 0; }