大意:网络流求最大流(模板题)
下面是对最大流的理解:
假如说有A,B,C,D三个点,A->B 有3 的流量,B->D有2的流量,A->C有3的流量,C->D有2的流量,B->C有3的流量,那么如何求A->D的最大流量呢?如图所示:
很明显用肉眼能看出来A->B->D+A->C->D=4就是答案,那么如何用程序来实现呢?
假如使用搜索的方法(bfs,dfs都行,但是一般来说bfs会比dfs快,并且这道题用dfs会超时),从A开始搜索可以走的边,搜索到B,C,然后以他们为起点继续搜索,搜索到D,达到终点,再把每条线路中路线的最小值加起来(2+2),就是答案了,吗?
有一种情况,假如程序在以B为起点的时候搜索,得到C,然后以C为起点搜索,进入D,噢,刚好一条线,answer=2,没了。
机器搜索得出来的路线不一定就是最优解,那么如何解决这个问题呢。。
答案就是在计算机每走一条边的时候为那条边加上一条反向的边,数值等于原来那条边上使用的流量,如图
这样机器在下一次搜索的时候,就依然可以得到A->C->B->D这条路,answer=4,就是答案了。那么为什么这么做是正确的呢
我们可以用最小割来证明,但是现在先用一种我自己的理解来解释。
我们只有找到一条完整的从起点到终点的路的时候,才可以算是找到一条路,因此假如我生成了一条从C->B的反向路,要是找不到从B到终点的路,那么这一条反向路也用不到,因此要用到生成的反向路必须在反向路的另一端能够到达终点,也就是B要到达终点。假如B能到达终点,那么 A->B->C->D 和 A->C->B->D 这两条路是不是等于 A->B->D 等于 A->C->D 这两条路了呢,毕竟你将两种走法相比较,bc 和 cb 因为这两条路的使用量是一样的(都是2),也就是总的数值都没变。
下面说说我对Edmond-Karp算法的理解
算法使用的是bfs,从起点开始宽搜,找到下一个点之后记录从点的移动轨迹
比如这道题的话使用一个pre数组,从A搜索到B之后让pre[B]=A
接着每搜寻到一条边,就找到整条边的最小值(A->B->D 最小值就是2),累计答案,并且在原图修改最小值且建立相反边(通过pre数组)
如此重复直到找不到边
输出答案
下面是那道题目的代码
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int N,M;
int flow[17][17],min_flow[17],pre[17];//flow的话时记录全体的边,min_flow[i]代表i以及i之前的边的最小值
int BFS(){
memset(pre,-1,sizeof(pre)); //每次找都要重置
queue<int> Q;
Q.push(1);
min_flow[1]=INF;
while(!Q.empty()){
int ind=Q.front();
Q.pop();
for(int i=1;i<=N;i++){
if(i!=1&&flow[ind][i]>0&&pre[i]==-1){
Q.push(i);
min_flow[i]=min(min_flow[ind],flow[ind][i]); //min_flow 转移方程
pre[i]=ind;
}
}
}
if(pre[N]==-1) return 0;
else return min_flow[N];
}
int main(){
int T; cin>>T;
for(int cnt=1;cnt<=T;cnt++){
memset(flow,0,sizeof(flow));
scanf("%d %d",&N,&M);
for(int i=1;i<=M;i++){
int x,y,c; scanf("%d %d %d",&x,&y,&c);
flow[x][y]+=c; //防止给重复的边,此时要加上,比如 1 3 2和1 3 2 那么就是 1 3 4
}
int delta=-1,max_flow=0; //delta 是每次找到一条边时边的最小值
while(delta=BFS(),delta!=0){
memset(min_flow,INF,sizeof(min_flow));
int sto=N;
while(pre[sto]!=-1){
flow[pre[sto]][sto]-=delta;
flow[sto][pre[sto]]+=delta;
sto=pre[sto];
}
max_flow+=delta;
}
printf("Case %d: %d\n",cnt,max_flow);
}
return 0;
}