給定指定的乙個有向圖,其中有兩個特殊的源點s和匯點t,每條邊有指定的容量,求滿足條件的從s到t的最大流。
殘量網路=容量網路-流量網路
概念就不講了吧,顧名思義。
增廣路: 設 f 是乙個容量網路 g 中的乙個可行流, p 是從 vs 到 vt 的一條鏈, 若 p 滿足下列條件:
則稱 p 為關於可行流 f 的一條增廣路, 簡稱為 增廣路(或稱為增廣鏈、可改進路)。沿著增廣路改進可行流的操作稱為增廣
割,割集
對於一張流量圖g,斷開一些邊後,源點s和匯點t就不在連通,我們將這樣的k條邊的權值(即最大容量)和求和,求和後的值稱為割。顯然,對於一張流量圖g,割有很多個且不盡相同。我們要求的就是所有割中權值最小的那乙個(可能不唯一),即花最小的代價使s和t不在同一集合中。
最小割最大流定理ff方法(ford-fulkerson)
根據增廣路定理, 為了得到最大流, 可以從任何乙個可行流開始, 沿著增廣路對網路流進行增廣, 直到網路中不存在增廣路為止,這樣的演算法稱為增廣路演算法。問題的關鍵在於如何有效地找到增廣路, 並保證演算法在有限次增廣後一定終止。
ff方法的基本流程是 :
反向弧建立的意義:為程式提供反悔的機會
很明顯,上圖最大流應該是2,但我們找到了一條錯誤的路徑,於是我們就應該有返回的機會,即建立反向邊,這樣再次從反向邊流過就相當於抵消了。
演算法思路
在ek演算法中, 程式的實現過程與增廣路求最大流的過程基本一致. 即每一次更新都進行一次找增廣路然後更新路徑上的流量的過程。但是我們可以從上圖中發現乙個問題, 就是每次找到的增廣路曲曲折折非常長, 此時我們往往走了冤枉路(即:明明我們可以從源點離匯點越走越近的,可是中間的幾條邊卻向離匯點遠的方向走了), 此時更新增廣路的複雜度就會增加。ek 演算法為了規避這個問題使用了 bfs 來尋找增廣路, 然後在尋找增廣路的時候總是向離匯點越來越近的方向去尋找下乙個結點。
複雜度\(\vartheta(m^n)\)
**
#include#include#include#include#define inf 0x7fffffff
#define n 10010
#define m 100010
using namespace std;
int n,m,ss,tt;
struct edgee[m<<1];
struct prepre[m<<1];//pre[i].node表示編號為i的點最短路的上乙個點,pre[i].id表示最短路上連線i點的邊的編號
int head[n],cnt=-1;//編號從0開始,原因見下
bool vis[n];
queueq;
void add(int from,int to,int value)
bool bfs(int s,int t)//用來尋找s,t的最短路並記錄,如果s,t不連通則返回0
} }return 0;
}int ek(int s,int t)
ans+=minv;
} return ans;
}int main()
printf("%d\n",ek(ss,tt));
return 0;
}
其實dinic演算法是ek演算法的改進
演算法思路
發現在ek演算法中,每增廣一次都要先進行bfs尋找最短增廣路,然而bfs後,很可能不止一條路徑可以增廣,如果還是按照ek演算法的bfs一次增廣一條路,很顯然浪費了很多時間,這樣,我們讓bfs負責尋找增廣路徑,dfs計算可行的最大流。
下圖1點為s點,6點為t點,紅線代表尋找的路徑,藍線代表回溯的路徑:
圖4,再次bfs更新dis
再次bfs,發現s和t不連通,結束演算法
複雜度:
在普通圖中:\(\vartheta(n^m)\)
在二分圖中:\(\vartheta(m\sqrt)\)
**
#include#include#include#include#define n 10010
#define m 100010
#define inf 0x7fffffff
using namespace std;
int n,m,ss,tt;
int dis[n];
queueq;
struct edgee[m<<1];
int head[n],cnt=-1;
void add(int from,int to,int value)
bool bfs(int s,int t)//bfs功能和ek演算法的相似,不同的是dinic中的bfs要求出所有點到源點s的最短路dis[i]
} }return dis[t]!=-1;
}int dfs(int x,int t,int maxflow)//表示從x出發尋找到匯點t的增廣路,尋找到maxflow流量為止,並相應的增廣。返回值為實際增廣了多少(因為有可能找不到maxflow流量的增廣路)
return ans;
}int dinic(int s,int t)
int main()
printf("%d\n",dinic(ss,tt));
return 0;
}
當前弧優化
我們知道dinic演算法中的dfs是為了在可行增廣路中找到最小容量並進行增廣。而找增廣路需要遍歷每個點所連線的邊,直至找到一條可到達終點的路。如果這一次找到了增廣路,下一次在訪問到這個點時,上一次已經檢查過的邊就不用再走一遍了,因為遍歷乙個點連線的邊都是有一定順序的,上一次訪問到這個點已經確定那幾條邊是不可行的。於是,我們用cur[i]
來表示下一次遍歷邊時應該從那一條開始。
雖然漸進時間複雜度沒有發生變化,但實際應用中的確大大降低了dinic的常數
優化**(其他**不發生變化)
int cur[n];
int dfs(int x,int t,int maxflow)
return ans;
}int dinic(int s,int t)
return ans;
}
網路流的優化演算法還有isap(improved shortest augumenting path),最高標號預流推進(hlpp)等等,dinic在一般情況下已經夠用了,其他演算法自學請移步其他大佬部落格嘍。 網路流 (基礎學習)
流網路g v,e 是乙個有向圖,其中每條邊 u,v e 均有一非負容量 c u,v 0,規定 若 u v e,則 c u,v 0。網路中由兩個特殊點 源點s和匯點t 網路中的邊相當於是粗細不同的水管,c u,v 相當於是水管的直徑。流網路g的流是乙個實值函式 f v v r,且滿足下列三個性質 1 ...
網路流基礎演算法模板
網路流是一種非常玄妙的演算法,被廣泛地用於各種有權值存在或一對多的匹配問題中。而網路流又有許多數學性質,比如最大流等於最小割等等。本篇主要介紹常用的dinic最大流演算法。網路,就是一張有點有邊圖。其中有兩個特殊的點 源點和匯點。網路流中的每一條邊就好比一條水管,容量就好比是這個水管的粗細。我們要求...
基礎網路流題單
1.luogu p2472 scoi2007 蜥蜴 題解2.luogu p2604 zjoi2010 網路擴容 題解3.luogu p3931 sac e 1 一道難題 tree 題解4.luogu p2763 試題庫問題 題解5.luogu p2766 最長不下降子串行問題 題解二分圖最大匹配 二...