最大流模板(EK,Dinic

一、EK

EK算法:用bfs找增广路直到找不到为止。找到则更新最大流和残余网络,找不到则结束。

残余网络:对于一条走过的边,其正向边权值减少相应值,反向边权值增加相应值(用于反悔)。

增广路:从所求起点到终点之间还可以增大流量的路径。

复杂度O(n*m^2)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,s,t;
const int maxn=220;
ll G[maxn][maxn],flow[maxn],pre[maxn];//flow:源点到当前点的流量,pre增广路的上一条边
ll bfs(int s,int t){//找增广路
    queue<int>qu;
    while(!qu.empty())qu.pop();
    memset(pre,-1,sizeof pre);//记录前驱
    pre[s]=0;
    flow[s]=0x3f3f3f3f;
    qu.push(s);
    while(!qu.empty()){
        int p=qu.front();qu.pop();
        if(p==t)break;
        for(int i=1;i<=n;i++){
            if(i!=s&&G[p][i]>0&&pre[i]==-1){
                pre[i]=p;
                flow[i]=min(flow[p],G[p][i]);//选承载量最小的
                qu.push(i);
            }
        }
    }
    if(pre[t]==-1)return -1;
    return flow[t];
}
ll EK(int s,int t){
    ll ans=0,tot=0;
    while(1){
        ans=bfs(s,t);
        if(ans==-1)break;
        int p=t;
        while(p!=s){//回溯整条增广路,进行更新
            G[pre[p]][p]-=ans;
            G[p][pre[p]]+=ans;//反向边
            p=pre[p];
        }
        tot+=ans;
    }
    return tot;
}
int main()
{
    int i,j;
    cin>>n>>m>>s>>t;
    memset(G,0,sizeof G);
    memset(flow,0,sizeof flow);
    for(i=0;i<m;i++){
        int a,b;ll c;cin>>a>>b>>c;
        G[a][b]+=c;//累计容量 防止重边
    }
    cout<<EK(s,t);
    return 0;
}
View Code

 二、Dinic

有时候EK会超时 因为可能会出现增广路经过的其中一条边值为1,而其他边值很大的情况,则需要一直增广。

而Dinic利用分层可以一次dfs实现多次增广,从而优化EK算法。

Dinic算法:先利用bfs进行分层(只能往层数+1的地方走),再利用dfs实现进行增广(一次dfs实现多次增广)。该步骤一直循环直到不可分层为止。

复杂度O(m*n^2)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50005;
const int maxm=500010;//
const int inf =0x3f3f3f3f;
int head[maxn],dis[maxn];
struct Edge
{
    int to,next,f;
}edge[maxm]; //链式前向星
int s,t,cnt;
void add(int u,int v,int f)
{
    edge[cnt].to=v;
    edge[cnt].f=f;
    edge[cnt].next=head[u];
    head[u]=cnt++; //正向建边//相邻边则为反向边,cnt从0开始(1不行)
    edge[cnt].to=u;
    edge[cnt].f=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;  //反向建边
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue <int> que;
    dis[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            int f=edge[i].f;
            if(dis[v]==-1&&f>0)//有流量且未访问过
            {
                dis[v]=dis[u]+1;//分层
                if(v==t) return true;
                que.push(v);
            }
        }
    }
    return false;
}
int dfs(int x,int maxf) //maxf表多少流量流到当前节点
{
    if(x==t||maxf==0) return maxf;
    int flow=0;
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        int f=edge[i].f;
        if(dis[v]==dis[x]+1&&f>0)
        {
            f=dfs(v,min(f,maxf-flow));//当前边的容量和该点剩余量取min
            edge[i].f-=f;
            edge[i^1].f+=f;//相邻边则为反向边,通过异或可以直接找到反向边
            flow+=f;
            if(flow==maxf) return flow;
        }
    }
    return flow;
}
int main()
{
    int T,n,m,k;
    cin>>n>>m>>s>>t;
        cnt=0;
        memset(head,-1,sizeof(head));
        for(int i=0;i<m;i++)
        {
            int u,v,f;
            scanf("%d%d%d",&u,&v,&f);
            add(u,v,f);  //加边
        }
        int ans=0;
        while(bfs()) ans+=dfs(s,inf);
        cout<<ans<<endl;
}
View Code

 

经过bfs分层后有123三层,从s出发,只会往第二层的三个点依次进行dfs。dfs手动模拟即可理解,每次走过时记得更新残余网络。

 

posted @ 2022-03-08 09:44  -第4题-  阅读(32)  评论(0编辑  收藏  举报