网络流最大流(Edmonds-Karp)

Edmonds-Karp

根据定义,若一条从源点S到汇点T的路径上各点的剩余容量都大于0,则称这条路径为增广路。那么就可以通过不断寻找增广路来求出最大流。
具体做法就是通过bfs寻找从S到T的增广路径,并顺便计算出最小剩余容量minx,找到后路径上各点减去minx,答案(最大流)加上minx。
需要注意的是,因为是单纯的遍历所有剩余流量大于零的边,所以无法保证当前所选的边是否是最优,所以需要一种方法可以“后悔”,即撤回之前的决定,而这种“后悔”的方法就是退流,即在遍历时除了考虑当前遍历的边以外,还需要考虑每条边的反向边。
具体实现时,我们按照邻接表成对储存技巧,在存储边时下标从2开始,那么反向边和边之间可以通过xor 1来转换。

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,m,S,T,linkk[205],t=1;
int q[505],head,tail;
int before[205],minx[205];
long long ans=0;
struct node{
  int y,v,n;
}e[405];
bool vis[205];
inline int read(){
  int NUM=0,f=1;
  char c=getchar();
  for(;c<'0'||c>'9';c=getchar())
  if(c=='-')f=-1;
  for(;c>='0'&&c<='9';c=getchar())
  NUM=(NUM<<1)+(NUM<<3)+c-48;
  return NUM*f;
}//快读
void insert(int xx,int yy,int zz){
  e[++t].y=yy;
  e[t].v=zz;
  e[t].n=linkk[xx];
  linkk[xx]=t;
  e[++t].y=xx;
  e[t].v=0;
  e[t].n=linkk[yy];
  linkk[yy]=t;
  return;
}//正反存边
void init(){
  n=read();m=read();S=read();T=read();
  for(int i=1;i<=n;++i){
    int x,y,z;
    x=read();y=read();z=read();
    insert(x,y,z);
  }
  return;
}
void Up(){
  int now=m;
  while(now!=S){
    int i=before[now];
    e[i].v-=minx[m];
    e[i^1].v+=minx[m];
    now=e[i^1].y;
  }
  ans+=minx[m];
  return;
}
bool BFS(){
  memset(vis,0,sizeof(vis));
  head=1;tail=0;
  q[++tail]=S;
  vis[S]=1;
  minx[S]=1e9;
  for(;head<=tail;++head){
    int x=q[head];
    for(int i=linkk[x];i;i=e[i].n){
      if(!e[i].v)continue;
      int y=e[i].y;
      if(vis[y])continue;
      vis[y]=1;
      minx[y]=min(minx[x],e[i].v);//更新最小剩余容量
      q[++tail]=y;
      before[y]=i;
      if(y==T)     return 1;//遍历到终点,找到一条路径
    }
  }
  return 0;
}
void work(){
  while(BFS())Up();
  cout<<ans;
  return;
}
int main(){
  init();
  work();
  return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_39671609/article/details/80962230