Description
Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph.
Input
The first line of input contains an integer T, denoting the number of test cases.
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)
Output
For each test cases, you should output the maximum flow from source 1 to sink N.
Sample Input
2 3 2 1 2 1 2 3 1 3 3 1 2 1 2 3 1 1 3 1
Sample Output
Case 1: 1 Case 2: 2
最大流的一些基本概念:
1 流量flow: u->v 的流量为f,则相反方向的v->u的流量为-f
2 容量cap:u->v的容量为c,但v->u的容量为0.(本来图中不存在的边容量都为0)
3 残量cp=容量-流量。cp(u->v)=cap(u->v)-flow(u->v);
4 增广路:在残量图中,一条从起点s到终点t的路径,且该路径上的所以残量的最小值要大于0.
反向边的作用:
反向边相当于一条后悔边,比如说你走了v->u的流量为d,相当于把u->v的流量减少了d
最大流ekrap算法的思想:(其实就是找增广路一步步逼近最大流)
1不停的找增广路,直到没有增广路。
2每找到一条增广路,最大流+=增广路的最大残量值。
3每找到一条增广路,就要更新残量图。(为了方便,我在下面的代码中每次都是更新流量,因为残量=容量-流量,容量不变,知道流量,也就知道了残量)
那么如何找一条从s到t的路径呢?如果随意找,可以用dfs,但是有可能会有特别慢的情况,比如转了好几个弯才到达t。
最好使用bfs找,最慢的时候也是s到t的最短距离。
模板1:dfs
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define maxn 1005
#define inf 0x3f3f3f3f
int first[maxn],nxt[maxn<<1],to[maxn<<1],pre[maxn<<1],e,flow[maxn<<1],cap[maxn<<1];
int a[maxn],fa[maxn];
void add(int u,int v,int c){
to[e]=v;
pre[e]=u;
flow[e]=0;
cap[e]=c;
nxt[e]=first[u];
first[u]=e++;
}
int bfs(int s,int t){
int f=0;
fa[s]=-1;
queue<int>q;
while(true){
memset(a,0,sizeof a);
a[s]=inf;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=first[u];~i;i=nxt[i]){
int v=to[i],c=cap[i],fl=flow[i];
if(!a[v]&&c-fl>0){
q.push(v);
fa[v]=i;
a[v]=min(a[u],c-fl);
}
}
}
if(a[t]==0)break;
int u=t;
while(fa[u]!=-1){
int i=fa[u];
flow[i]+=a[t];
flow[i^1]-=a[t];
u=pre[i];
}
f+=a[t];
}
return f;
}
int main()
{
int t;
int n,m;
int u,v,c;
int tt=0;
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--){
e=0;
memset(first,-1,sizeof first);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,0);
}
printf("Case %d: %d\n",++tt,bfs(1,n));
}
}
模板2:bfs
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define maxn 1005
#define inf 0x3f3f3f3f
int first[maxn],nxt[maxn<<1],to[maxn<<1],pre[maxn<<1],e,flow[maxn<<1],cap[maxn<<1];
int a[maxn],fa[maxn];
bool vis[maxn];
void add(int u,int v,int c){
to[e]=v;
pre[e]=u;
flow[e]=0;
cap[e]=c;
nxt[e]=first[u];
first[u]=e++;
}
int dfs(int u,int t){ //这是错误的写法,不要被误导
if(u==t)return inf;
vis[u]=1;
for(int i=first[u];~i;i=nxt[i]){
int v=to[i];
if(!vis[v]&&cap[i]-flow[i]>0){
int d=min(cap[i]-flow[i],dfs(v,t)); //错误,最底层返回就要知道最小是多少了。不能全部返回完才知道
if(d>0){ //如果这样写,就得和bfs一样的记录路径的写法
flow[i]+=d;
flow[i^1]-=d;
return d;
}
}
}
return 0;
}
int dfs1(int u,int t,int f){
if(u==t)return f;
vis[u]=1;
for(int i=first[u];~i;i=nxt[i]){
int v=to[i];
if(!vis[v]&&cap[i]-flow[i]>0){
int d=dfs1(v,t,min(f,cap[i]-flow[i]));
if(d>0){
flow[i]+=d;
flow[i^1]-=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t){
int flow=0;
while(1){
memset(vis,0,sizeof vis);
int f=dfs1(s,t,inf);
if(f==0)return flow;
flow+=f;
}
}
int main()
{
int t;
int n,m;
int u,v,c;
int tt=0;
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--){
e=0;
memset(first,-1,sizeof first);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&c);
add(u,v,c);
add(v,u,0);
}
printf("Case %d: %d\n",++tt,max_flow(1,n));
}
}