网络流 EK增广路算法之一

//poj 1273 Drainage Ditches

#include
<iostream> //Edmonds-Karp算法
#include<queue>
using namespace std;
#define inf 0x7fffffff
int flow[202][202],cap[202][202],a[202],p[202],s,t,f; //s,t为源点和汇点,f保存s-t最大流的流量
//cap记录容量限制,flow记录当前的流量
//a[i]记录s-i路径上的最小残余容量(边的容量与当前流量之差)
//p[i]=u,记录s-t增广路径上结点i的前驱结点是u
int main()
{
int n,m; //结点下标:1<=i<=m
while(cin>>n>>m)
{
memset(cap,
0,sizeof(cap));
int si,ei,ci;
while(n--)
{
cin
>>si>>ei>>ci;
cap[si][ei]
+=ci; //该题中,边是有向的,而且可能重复
}
s
=1;t=m;
queue
<int> col;
memset(flow,
0,sizeof(flow));
f
=0;
while(1) //找s-t的一条增广路,求出该路径中所有残余容量的最小值,将这最小值加到该路径的所有边上,即为增广
{
memset(a,
0,sizeof(a)); //初始化残余容量数组为0
a[s]=inf; //a[s]初始化为无穷大,在下面的a[i]=min(a[u],cap[u][i]-flow[u][i]);可以体现出来
col.push(s);
while(!col.empty()) //BFS找增广路
{
int u=col.front();col.pop();
for(int i=1;i<=m;++i)
if(!a[i]&&cap[u][i]>flow[u][i]) //找到新结点i,由于a[i]总是正数,可以用它代替原来的vis标志数组,所以如果a[i]=0,则说明是新结点
{
a[i]
=min(a[u],cap[u][i]-flow[u][i]); //a[i]表示s-i路径上的最小残余容量,u是i的前驱结点
p[i]=u; col.push(i); //记录i的父亲u,并将i加入队列中
//注意到u有多个后继结点,所以p[i]只能记录i的前驱结点,而不能以p[u]=i来表示后继结点
}
}
if(a[t]==0) //找不到增广路,则当前流已是最大流
break;
for(int i=t;i!=s;i=p[i]) //从汇点往回走
flow[p[i]][i]+=a[t],flow[i][p[i]]-=a[t]; //更新正反流量
f+=a[t]; //更新从s流出(即流入t)的总流量
}
cout
<<f<<endl;
}
return 0;
}









/*
一个无向连通网络,去掉一个边集可以使其变成两个连通分量则这个边集就是割集;最小割集当然就是权值和最小的割集。
最小割最大流定理:网络的最大流等于最小割,证明如下:
(1).显然,任意一个流都小于等于任意一个割,
(2).而我们可以构造出一个流等于一个割:
当达到最大流时 根据增广路定理,残留网络中s到t已经没有通路了 否则还能继续增广
我们把s能到的的点集设为S 不能到的点集为T,构造出一个割集C[S,T]
S到T的边必然满流 否则就能继续增广,这些满流边的流量和就是当前的流即最大流
把这些满流边作为割,就构造出了一个和最大流相等的割.
由(1)(2)可得最大流等于最小割


*/

  

posted on 2011-07-17 01:34  sysu_mjc  阅读(311)  评论(0编辑  收藏  举报

导航