Description
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.
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 Output
50
最大流 Edmonds-Karp算法
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
LL flow[210][210],M,N,cap[210][210],father[210],a[210];//cap容量 当前flow流量
void readdata()
{
memset(flow,0,sizeof(flow));
memset(cap,0,sizeof(cap));
for(int i=1;i<=M;i++)
{
LL a,b,c;
scanf("%I64d%I64d%I64d",&a,&b,&c);
cap[a][b]+=c;//注意有重边
}
}
void EK()
{
memset(flow,0,sizeof(flow));
LL ans=0;
while(1)
{
memset(a,0,sizeof(a)); a[1]=INF;//a[i]表示1到i的增广量
queue<LL> q;
q.push(1);
while(!q.empty())//BFS找到的增广路一定是最短的(即边数最少)
{
int now=q.front();q.pop();
for(LL i=1;i<=N;i++)
if(!a[i] && cap[now][i]>flow[now][i])
{
father[i]=now;
q.push(i);
a[i]=min(a[now],cap[now][i]-flow[now][i]);
}
}
if(a[N]==0) break;
for(LL now=N;now!=1;now=father[now])
{
flow[father[now]][now]+=a[N];
flow[now][father[now]]-=a[N];
}
ans+=a[N];
}
printf("%I64d\n",ans);
/*
long long sum=0;
for(int i=1;i<=N;i++) sum+=flow[i][N];
printf("%I64d\n",sum);
*/
}
int main()
{
freopen("ditch.in","r",stdin);
freopen("ditch2.out","w",stdout);
while(scanf("%I64d%I64d",&M,&N)==2)////N个节点M条边
{
readdata();
EK();
}
return 0;
}
距离标记最短增广路算法(SAP)
其实大致思路就是每次增广最短路径, 维护一个dis数组表示每个点到汇点的最小跳数,dis数组必须有这个性质: dis[i]<= dis[j] + 1 ( r[i][j] > 0);定义一条允许弧:如果满足:dis[i] = dis[j] + 1( r[i][j] >0);那么连接i,j的边就叫允许弧, 书上有这个定理:从源点到汇点的最短路一定是用允许弧构成。所以每次扩展路径都找允许弧,如果i没有允许弧就更新dis[i] = min{ dis[j] + 1 | r[i][j]> 0); 如此容易知道当dis[source] = n是就增广完毕,因为从源点到汇点最多有n-1步。最后dis数组可以用一个BFS初始化,但是实际操作中dis往往被初始化为0。
//sap gap优化版
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long LL;
long long vd[210],dis[210],flow[210][210],F[210][210];//dis:标号距离, 数组vd[i]记录标号距离为i的顶点个数,flow为残量网络
long long M,N;
void readdata()
{
memset(flow,0,sizeof(flow));
memset(dis,0,sizeof(dis));
memset(vd,0,sizeof(vd));
for(int i=1;i<=M;i++)
{
int a,b;LL x;
scanf("%d%d%I64d",&a,&b,&x);
flow[a][b]+=x;
}
}
long long dfs(LL now,LL f)//返回now到终点N的增广量
{
if(now==N) return f;//找到增广路
LL ans=0;
for(int i=1;i<=N;i++)
if(flow[now][i]>0 && dis[now]==dis[i]+1)//是允许弧
{
LL tmp=dfs(i,min(f-ans,flow[now][i]));//tmp是下一个点到终点N的增广量
flow[now][i]-=tmp; flow[i][now]+=tmp;
ans+=tmp;
if(ans==f) return ans;
}
if(dis[1]>=N) return ans;//如果起点的标号距离大于N,就不存在增广路
vd[dis[now]]--;
if(vd[dis[now]]==0) dis[1]=N;//如果标号距离是唯一的,删除该点后出现断层不再有可行弧(gap优化)。以d[1]为标记
dis[now]++;//向上提一层
vd[dis[now]]++;
return ans;
}
void SAP()
{
vd[0]=N;
LL ans=0;
while(dis[1]<N) ans+=dfs(1,INF);
printf("%I64d\n",ans);
}
int main()
{
freopen("ditch.in","r",stdin);
while(scanf("%I64d%I64d",&M,&N)==2)
{
readdata();
SAP();
}
while(1);
return 0;
}
广搜标号法(dinic)
先广搜标一下等级,相邻等级才能有流量。每次dinic的标号是每次全图标号,然后一次深搜全部增广,然后再残量图标号……比起SAP,dinic每次可以找到多条增广路,效率上来讲,一般会更高效一点
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define maxN 210
#define INF 0x3f3f3f3f
int N,M,Map[maxN][maxN],Deep[maxN];
void readdata()
{
memset(Map,0,sizeof(Map));
for(int i=1;i<=M;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Map[a][b]+=c;
}
}
bool bfs()//广搜标号
{
memset(Deep,0,sizeof(Deep));
queue<int> q;
q.push(1); Deep[1]=1;
while(!q.empty())
{
int v=q.front(); q.pop();
for(int u=1;u<=N;u++)
if(!Deep[u] && Map[v][u]>0)
{
q.push(u);
Deep[u]=Deep[v]+1;
if(u==N) return 1;
}
}
return 0;
}
int dfs(int v,int Flow)//进行一次增广
{
if(v==N || Flow==0) return Flow;
int Cap=Flow;//Cap指当前剩余量
for(int u=1;u<=N;u++)
if(Deep[v]+1==Deep[u] && Map[v][u]>0)
{
int x=dfs(u,min(Map[v][u],Cap));
Cap-=x;
Map[v][u]-=x; Map[u][v]+=x;
if(Cap==0) return Flow;
}
return Flow-Cap;
}
int dinic()
{
int sum=0;
while(bfs()) sum+=dfs(1,INF);//每次对残量网络进行广搜标号和增广,直到不能广搜标号为止
return sum;
}
int main()
{
freopen("ditch.in","r",stdin);
while(scanf("%d%d",&M,&N)==2)
{
readdata();
printf("%d\n",dinic());
}
while(1);
return 0;
}