題目鏈接:http://poj.org/problem?id=1273
解析:求最大流的一題入門題,使用的是效率不太高的EK算法,不會ek的話可以研究一下這個博客
http://www.cnblogs.com/devil-91/archive/2012/08/17/2644569.html註釋的很詳細
關於反向邊爲什麼加最大流,可以看下面,(轉載)
----------------------------------------------------我是分割線~\(≧▽≦)/~--------------------------------------------------
在這幅圖中我們首先要增廣1->2->4->6,這時可以獲得一個容量爲 2的流,但是如果不建立4->2反向弧的話,則無法進一步增廣,最終答案爲2,顯然是不對的,然而如果建立了反向弧4->2,則第二次能進行 1->3->4->2->5->6的增廣,最大流爲3.
Comzyh對反向弧的理解可以說是"偷樑換柱",請仔細閱讀: 在上面的例子中,我們可以看出,最終結果是1->2->5->6和1->2->4->6和 1->3->4->6.當增廣完1->2->4->6(代號A)後,在增廣 1->3->4->2->5->6(代號B),相當於將經過節點2的A流從中截流1(總共是2)走2->5>6,而不走2->4>6了,同時B流也從節點4截流出1(總共是1)走4->6而不是4->2->5->6,相當於AB流做加法.
簡單的說反向弧爲今後提供反悔的機會,讓前面不走這條路而走別的路.
//EK算法
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define Max 1000000000
using namespace std;
int n,m,ans;
int cap[210][210],pre[210],flow[210]; //pre[]存的是該點的前驅,flow[]是起始點到該點的最大流
int bfs(int start,int end) //每一次bfs,就找到一條由start到end的路
{
queue<int>Q;
while(!Q.empty())
Q.pop();
memset(pre,-1,sizeof(pre));
memset(flow,0,sizeof(flow));//初始化
flow[start] = Max;
pre[start] = 0;
Q.push(start);
while(!Q.empty())
{
int temp = Q.front();
Q.pop();
if(temp == end)
break;
for(int i = 1; i <= m;i++)
{
if(cap[temp][i] > 0 &&pre[i] == -1) //pre[i]==-1說明他還沒進入過隊列,保證bfs不會往回走
{
pre[i] = temp;
flow[i] = min(flow[temp],cap[temp][i]);
Q.push(i);
}
}
}
if(flow[end] == 0) //代表途中已經沒有start到end的路了
return 0;
else
return flow[end];
}
void ek(int start,int end)
{
while(1)
{
int t = bfs(start,end); //t存儲的是該路的最大流
if(t == 0)
break;
for(int k = end; k != start; k = pre[k])
{
cap[pre[k]][k] -= t; //正向邊減去最大流
cap[k][pre[k]] += t; //反向邊加上最大流
}
ans += t; //每一條邊都要加上纔是最大流
}
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF)
{
memset(cap,0,sizeof(cap));
memset(pre,0,sizeof(pre));
memset(flow,0,sizeof(flow));
int a,b,c;
ans = 0;
for(int i = 1; i <= n;i++)
{
scanf("%d%d%d",&a,&b,&c);
cap[a][b] += c; //可能有多個a->b的邊,則a->b的流量就是全部加在一起
// cap[b][a] -= c; //所有邊的反向邊都是0,若爲-c的話,正向回來就是c,正向的流量就變成2*c了
}
ek(1,m);
printf("%d\n",ans);
}
return 0;
}