Drainage Ditches
Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch.
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network.
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.
INPUT:
The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.
OUTPUT:
For each case, output a single integer, the maximum rate at which water may emptied from the pond.
SAMPLE INPUT:
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
SAMPLE OUPUT:
50
这一道题是网络流的模板题,再写这道题之前我们还需要了解一下流网络,流网络其实是一种图,不过流网络只有
一个源点和一个终点,除此之外每条边上还有一个权值,即能流过这条边的最大容量,我们可以把流网络想象成一
下水管道,能流到终点的最大容量即为流网络的最大流。
求解一个流网络的最大流有许多种方法,在这里我们只介绍FF算法
Ford-Fulkerson 算法是一种解决最大流的方法,其主要还是依赖于增广路的寻求:
找到一条从源点到汇点的任意路径,所有边上的最小值delta如果不是0,那么总流量就可以增加delta,
在将路径上的边的容量减去delta,这就是一条增广路。 易知,如果找不出增广路了,那么此时的流量就是最大了。
但是,就这样还不行,如果“走错了”怎么办? 现在引进反向弧,反向弧可以解决这个问题。其他的博客上很多说反向弧可以让流量后悔,
这个比喻很生动,但用我自己的话来说,反向弧因为是同原边反向的,两者中一者减去delta时,
将另一者加上delta,那么之后找增广路时,走反向弧相当于原边少走,非常巧妙
这种算法自己举个样列手动模拟一下就应该可以完全理解
————————————————————————————————————————————————————————————
#include<iostream> #include<string.h> #include<algorithm> #include<stack> #define maxn 205 #define inf 0x7f7f7f using namespace std; struct node { int next,to,w; }edge[maxn<<1]; int head[maxn],rec[maxn],pre[maxn],flow[maxn]; int cnt,m,n,ans=0; stack<int>sta; void init() { cnt=0; ans=0; memset(head,-1,sizeof(head)); } void add(int u,int v,int val) //最基本的链式前向星存图 { edge[cnt].next=head[u]; edge[cnt].to=v; edge[cnt].w=val; head[u]=cnt++; } int dfs(int s,int t) { memset(pre,-1,sizeof(pre)); while(!sta.empty()) sta.pop(); pre[s]=s,flow[s]=inf; sta.push(s); while(!sta.empty()) { int top=sta.top(); sta.pop(); int k=head[top]; while(k!=-1) { //cout<<"-1"<<endl; if(pre[edge[k].to]==-1&&edge[k].w>0) { flow[edge[k].to]=min(flow[top],edge[k].w); pre[edge[k].to] = top; rec[edge[k].to] = k; sta.push(edge[k].to); } k=edge[k].next; } if(pre[t]!=-1) return flow[t]; } return -1; } void ff(int s,int t) { int add; while((add=dfs(s,t))!=-1) { //cout<<"-1"<<endl; ans+=add; int k=t; while(k!=s) { edge[rec[k]].w-=add; edge[rec[k]^1].w+=add; k=pre[k]; } } } int main() { init(); while(cin>>m>>n) { init(); for(int i=1;i<=m;i++) { int a,b,c; cin>>a>>b>>c; add(a,b,c); add(b,a,0); } ff(1,n); cout<<ans<<endl; } return 0; }
后续的算法会在其他的博客中补进
另一篇讲最大流问题的博客:http://blog.sina.com.cn/s/blog_6cf509db0100uy5n.html
补充说明我对方向变的理解:
就比如说图中的这种情况 ,边2-3上的流量去了哪呢,原本从二流向三的流量改为了从2流向4,而从3流向4的流向的来源变为了1-3边上的流量
从而实现了增广的目的。