1. 程式人生 > >[模板]-網路最大流-Edmonds Krap

[模板]-網路最大流-Edmonds Krap

問題描述:
給出一個網路圖,以及其源點和匯點,求出其網路最大流。
程式碼:

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尋找增廣路,直到網路上不存在增廣路為止。其複雜度為O(nm2),然而在實際應用中則遠遠達不到這個上界,效率比較高。一般能夠處理1000-10000規模的網路
成對儲存:將一對反向邊相鄰地儲存,如e[0]與e[1]互為反向邊,e[2]與e[3]互為反向邊
e[i].w記錄的是剩餘容量
當一條邊(x,y)流過大小為e的流時,令(x,y)的剩餘容量減小e,(y,x)的剩餘容量增加e
反向邊是什麼啊。。。反向邊保證了最大流的正確性,反向邊是演算法的反悔的途徑。
在任意時刻,網路中所有的頂點以及剩餘容量大於0的邊構成的子圖被稱為殘量網路