前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EK (Edmond-Karp) 算法 学习笔记

EK (Edmond-Karp) 算法 学习笔记

作者头像
yzxoi
发布2022-09-19 12:43:54
7210
发布2022-09-19 12:43:54
举报
文章被收录于专栏:OI

EK (Edmond-Karp) 算法 学习笔记

什么是EK算法

EK (Edmond-Karp) 算法,说白了就是求最大流/费用流之类的问题的算法。

什么是最大流

定义

带权的有向图G=(V,E),满足以下条件,则称为网络流图(flow network)

  1. 仅有一个入度为0的顶点s,称s为源点。
  2. 仅有一个出度为0的顶点t,称t为汇点。
  3. 每条边的权值都为非负数,称为该边的容量,记作c(i,j)
  4. 通过容量网络G中每条弧< u,v>f(u,v),称为弧的流量。

简单来说就是水流从一个源点s通过很多路径,经过很多点,到达汇点t,问你最多能有多少水能够到达t点。 从st经过若干个点,若干条边,每一条边的水流都不能超过边权值(可以小于等于但不能大于) (由于是来学EK算法的,最大流什么的详细解读就不说了)

例题

Luogu P3376 【模板】网络最大流

题意

给出一个网络图,以及其源点和汇点,求出其网络最大流。

思路

这是最大流的模板题,请往下阅读。

增广路

增广路定义

增广路是指从源点s到汇点t的一条路,流过这条路,可以使得当前的流可以增加。

如何求增广路

其实就是从源点s开始bfs即可,到达汇点t时,然后找到这个路径的权值的最小的边,然后把路径上的每一条边减去这个最小值即可。

Code

寻找增广路
代码语言:javascript
复制
queue<int> q;//队列
bool bfs(){
    while(!q.empty()) q.pop();//清空
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    vis[s]=1;//源点标记
    q.push(s);//入队
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=fir[u];i;i=nxt[i]){
            int to=son[i];
            if(vis[to]==0&&w[i]!=0){
                pre[to].v=u;
                pre[to].edge=i;//记路径
                if(to==t) return 1;//到达t点
                vis[to]=1;
                q.push(to);//拓展
            }
        }
    }
    return 0;
}
定义
代码语言:javascript
复制
int n,m,s,t,vis[MAXN];
int fir[MAXN],nxt[MAXN],w[MAXN],son[MAXN],tot=1,ans;//领接表
struct edge{
    int v,edge;//用来记路径
}pre[101010];
EK算法核心(你别急,我还没说完)
代码语言:javascript
复制
int EK(){
    ans=0;
    while(bfs()){
        int Min=2e9;
        for(int i=t;i!=s;i=pre[i].v){
            Min=min(Min,w[pre[i].edge]);
        }
        for(int i=t;i!=s;i=pre[i].v){
            w[pre[i].edge]-=Min;
        }
        ans+=Min;
    }
    return ans;
}

这样很明显是错误的。。。

反向边

为什么要建反边

为什么不建反边(逃: 非常显然,如果第一次流错了使其无法得到最大流怎么办? 就比如这张图:

然而bfs沙雕的选了上面一条路怎么办。。。 所以这时候就需要反向边出场了 一开始用反向边建一个比边权为0的边。就像这样↓

然后每次bfs以后给相应的反边加上流量,正边减去流量。

Code

代码语言:javascript
复制
int EK(){
    ans=0;
    while(bfs()){
        int Min=2e9;
        for(int i=t;i!=s;i=pre[i].v){
            Min=min(Min,w[pre[i].edge]);
        }
        for(int i=t;i!=s;i=pre[i].v){
            w[pre[i].edge]-=Min;//减去流量
            w[pre[i].edge^1]+=Min;//加上流量
        }
        ans+=Min;
    }
    return ans;
}

回归例题

所以这道最大流的模板题代码:

Code

代码语言:javascript
复制
#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;
#define MAXN 200010
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,m,s,t,vis[MAXN];
int fir[MAXN],nxt[MAXN],w[MAXN],son[MAXN],tot=1,ans;
struct edge{
    int v,edge;
}pre[101010];
void add(int x,int y,int z){
    ++tot;
    nxt[tot]=fir[x];
    fir[x]=tot;
    w[tot]=z;
    son[tot]=y;
}
queue<int> q;
bool bfs(){
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=fir[u];i;i=nxt[i]){
            int to=son[i];
            if(vis[to]==0&&w[i]!=0){
                pre[to].v=u;
                pre[to].edge=i;
                if(to==t) return 1;
                vis[to]=1;
                q.push(to);
            }
        }
    }
    return 0;
}
int EK(){
    ans=0;
    while(bfs()){
        int Min=2e9;
        for(int i=t;i!=s;i=pre[i].v){
            Min=min(Min,w[pre[i].edge]);
        }
        for(int i=t;i!=s;i=pre[i].v){
            w[pre[i].edge]-=Min;
            w[pre[i].edge^1]+=Min;
        }
        ans+=Min;
    }
    return ans;
}
int main(){
    n=read();m=read();s=read();t=read();
    for(int x,y,z,i=1;i<=m;i++){
        x=read();y=read();z=read();
        add(x,y,z);
        add(y,x,0);
    }
    write(EK());putchar('\n');
    return 0;
}

EK算法拓展

费用流问题

Luogu P3381 【模板】最小费用最大流

Code

代码语言:javascript
复制
#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define int long long 
using namespace std;
#define MAXN 200010
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,m,s,t,vis[MAXN],dis[MAXN],anss;
int fir[MAXN],nxt[MAXN],w[MAXN],son[MAXN],tot=1,ans,cost[MAXN];
struct edge{
    int v,edge;
}pre[101010];
void add(int x,int y,int z,int c){
    ++tot;
    nxt[tot]=fir[x];
    fir[x]=tot;
    w[tot]=z;
    son[tot]=y;
    cost[tot]=c;
}
queue<int> q;
bool spfa(){
    while(!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    memset(dis,63,sizeof(dis));
    dis[s]=0;dis[t]=2e9;
    vis[s]=1;q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=fir[u];i;i=nxt[i]){
            int to=son[i];
            if(w[i]>0&&dis[to]>dis[u]+cost[i]){
                dis[to]=dis[u]+cost[i];
                pre[to].edge=i;
                pre[to].v=u;
                if(vis[to]==0){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }
    if(dis[t]<2e9) return 1;
    return 0;
}
int EK(){
    ans=0;anss=0;
    while(spfa()){
        int Min=2e9;
        for(int i=t;i!=s;i=pre[i].v){
            Min=min(Min,w[pre[i].edge]);
        }
        for(int i=t;i!=s;i=pre[i].v){
            w[pre[i].edge]-=Min;
            w[pre[i].edge^1]+=Min;
        }
        anss+=Min;
        ans+=Min*dis[t];
    }
    return ans;
}
signed main(){
    n=read();m=read();s=read();t=read();
    for(int x,y,z,c,i=1;i<=m;i++){
        x=read();y=read();z=read();c=read();
        add(x,y,z,c);
        add(y,x,0,-c);
    }
    EK();
    write(anss);putchar(' ');
    write(ans);putchar('\n');
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-04-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • EK (Edmond-Karp) 算法 学习笔记
    • 什么是EK算法
      • 什么是最大流
        • 定义
        • 例题
      • 增广路
        • 增广路定义
        • 如何求增广路
        • Code
      • 反向边
        • 为什么要建反边
        • Code
      • 回归例题
        • Code
      • EK算法拓展
        • 费用流问题
        • Code
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档