[模板]-網路最大流-Edmonds Krap
阿新 • • 發佈:2019-02-03
問題描述:
給出一個網路圖,以及其源點和匯點,求出其網路最大流。
程式碼:
struct edge//鏈式前向星
{
int to;
int next;
int w;
};
edge e[200010];//邊
int head[10010];
int cnt = 0;//注意需要從0開始
int n,m,s,t;//頂點數、邊數、源點、匯點
bool book[10010];//標記陣列
int incf[10010],prev[10010],max_flow;//increase flow
void add_edge(int u,int v,int w)//加邊
{
e[cnt].to = v;
e[cnt].next = head[u];
e[cnt].w = w;
head[u] = cnt;
cnt++;
}
bool bfs()//尋找增廣路
{
memset(book,0,sizeof(book));//重置標記陣列
queue<int> q;
q.push(s);//將源點入隊
book[s] = true;//標記
incf[s] = inf;
while(!q.empty())
{
int st = q.front();//取出隊首頂點
q.pop();//出隊
for(int i = head[st]; i != -1; i = e[i].next)
{
int to = e[i].to;//頂點to
int w = e[i].w;//有向邊st-->to上的剩餘容量
if(w != 0)//如果剩餘容量不為0(大於0或小於0)
{
if(book[to] == true)//如果頂點to已被包含在路徑中
continue;
incf[to] = min(incf[st],w);//取所有邊剩餘容量的最小值,這個最小值才是能夠增加的流量,最後的incf[t]即為答案
prev[to] = i;//記錄邊st-->to的下標
q.push(to);//將頂點to入隊
book[to] = true;//標記
if(to == t)//如果到達了匯點
return true;//找到了一條增廣路
}
}
}
return false;
}
void update()
{
int x = t;//從匯點開始找前驅
while(x != s)
{
int i = prev[x];//頂點x與前驅頂點的邊的下標
e[i].w -= incf[t];//更新剩餘容量,正向邊+
e[i ^ 1].w += incf[t];//更新剩餘容量,反向邊-
x = e[i ^ 1].to;//反向邊的終點就是正向邊的起點,這樣就實現了找前驅的過程
}
max_flow += incf[t];//更新
}
int main()
{
cin >> n >> m >> s >> t;
memset(head,-1,sizeof(head));//初始化
for(int i = 1; i <= m; ++i)
{
int a,b,c;
cin >> a >> b >> c;
add_edge(a,b,c);
add_edge(b,a,0);//反向的容量為0的邊
}
while(bfs() == true)//只要找到了新的增廣路
update();//更新流量
cout << max_flow << endl;
return 0;
}
解決方法:
如果一條從源點S到匯點T的路徑上各條邊的剩餘容量都大於0,則稱這條路徑為一條增廣路
EK演算法只能求解有向圖上的最大流問題。
Edmonds-Krap演算法的思想就是不斷用BFS尋找增廣路,直到網路上不存在增廣路為止。其複雜度為,然而在實際應用中則遠遠達不到這個上界,效率比較高。一般能夠處理1000-10000規模的網路
成對儲存:將一對反向邊相鄰地儲存,如e[0]與e[1]互為反向邊,e[2]與e[3]互為反向邊
e[i].w記錄的是剩餘容量
當一條邊(x,y)流過大小為e的流時,令(x,y)的剩餘容量減小e,(y,x)的剩餘容量增加e
反向邊是什麼啊。。。反向邊保證了最大流的正確性,反向邊是演算法的反悔的途徑。
在任意時刻,網路中所有的頂點以及剩餘容量大於0的邊構成的子圖被稱為殘量網路