博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

网络最大流

洛谷(p3376)

题目描述

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

输入格式

第一行包含四个正整数 n,m,s,tn,m,s,t,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含三个正整数ui,vi ,wi,表示第 i条有向边从 u i出发,到达vi,边权为wi(即该边最大流量为wi)。

输出格式

一行,包含一个正整数,即为该网络的最大流。

输入输出样例

输入

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40

输出

50

说明/提示

样例输入输出 解释

题目中存在 3 条路径:

4→2→3,该路线可通过 20 的流量。

4→3,可通过 20 的流量。

4→2→1→3,可通过 10 的流量(边 4→2 之前已经耗费了 20 的流量)。 故流量总计 20+20+10=50。输出 50。

思路

这道题很显然是一道网络流当中的最大流的模板题,我们可以用ek算法来解决这道题目,用bfs来找到我们需要的增广路,来更新我们的最大流,(如果有增广路的话我们就在这条增广路上有一定的流量,就是当前的最大流变得更大,得到我们需要的正确答案),在找增广路的时候,我们需要注意在找了一条边过后我们应该建立这条边的相反路径,使其路径的容量增加我们找到的路径的减少量,这样可以方便我们反悔(当找不到路径时可以倒回去)

bfs核心代码

bool bfs(long long s,long long t){
    long long p;
    queue<long long>q;
    memset(pre,-1,sizeof(pre));
    memset(visit,false,sizeof(visit));
    pre[s]=s;
    visit[s]=true;
    q.push(s);
    while(!q.empty()){
        p=q.front();
        q.pop();
        for(long long i=1;i<=n;i++){
            if(r[p][i]>0&&!visit[i]){
                pre[i]=p;
                visit[i]=true;
                if(i==t) return true;
                q.push(i);
            }
        }
    }
    return false;
}

ek算法核心代码

long long EdmondsKarp(long long s,long long t){
   long long flow=0,d,i;
   while(bfs(s,t)){
       d=inf;
       for(i=t;i!=s;i=pre[i])
           d=min(d,r[pre[i]][i]);
       for(i=t;i!=s;i=pre[i]){
           r[pre[i]][i]-=d;
           r[i][pre[i]]+=d;
       }
       flow+=d;
   }
   return flow;
}

在这道题道中我们应该申明一个二维数组r【maxn】【maxn】,用r【a】【b】来存储我们的我们从a点到b点的容量,我们的流量必然小于等于我们每两个点之间的容量;用bool型的visit数组记录点是否被储存过,pre数组来记录我们当前节点的前驱节点,方便进行反悔操作。最后我们只需要跑一下ek算法(只需要我们给出源点【源点:只流出不流进的点】和汇点【汇点:只流进不流出的点】就行了),直接输出答案即可了。

代码(ek)

#include<bits/stdc++.h>
using namespace std;
const long long maxn=205;
const long long inf=0x7fffffff;
long long r[maxn][maxn];
bool visit[maxn];
long long pre[maxn];
long long m,n,s,t;
bool bfs(long long s,long long t){
    long long p;
    queue<long long>q;
    memset(pre,-1,sizeof(pre));
    memset(visit,false,sizeof(visit));
    pre[s]=s;
    visit[s]=true;
    q.push(s);
    while(!q.empty()){
        p=q.front();
        q.pop();
        for(long long i=1;i<=n;i++){
            if(r[p][i]>0&&!visit[i]){
                pre[i]=p;
                visit[i]=true;
                if(i==t) return true;
                q.push(i);
            }
        }
    }
    return false;
}
long long EdmondsKarp(long long s,long long t){
   long long flow=0,d,i;
   while(bfs(s,t)){
       d=inf;
       for(i=t;i!=s;i=pre[i])
           d=min(d,r[pre[i]][i]);
       for(i=t;i!=s;i=pre[i]){
           r[pre[i]][i]-=d;
           r[i][pre[i]]+=d;
       }
       flow+=d;
   }
   return flow;
}
int main(){
    cin>>n>>m>>s>>t;
    long long u,v,w;
    memset(r,0,sizeof(r));
    for(long long i=0;i<m;i++){
        cin>>u>>v>>w;
        r[u][v]+=w;
    }
    cout<<EdmondsKarp(s,t)<<endl;
    return 0;
}

 

记得开long long哦,如果用int的话会wa一些点


当然这道题还可以用dinic算法,dinic算法比ek算法更快,dinic算法就是ek算法的优化,主要的区别在于ek算法用bfs来查找我们的增广路,而dinic算法则使用dfs来查找我们的增广路,dfs的效率比bfs高得多,dfs一次可以找到多条增广路,而bfs要多次才能找到一条增广路。

代码(dinic)

#include <bits/stdc++.h>
using namespace std;
const long long inf=2005020600;
int n,m,s,t,u,v;
long long w,ans,dis[520010];
int tot=1,now[520010],head[520010];
struct node {
    int to,net;
    long long val;
} e[520010];

inline void add(int u,int v,long long w) {
    e[++tot].to=v;
    e[tot].val=w;
    e[tot].net=head[u];
    head[u]=tot;
    
    e[++tot].to=u;
    e[tot].val=0;
    e[tot].net=head[v];
    head[v]=tot;
}

inline int bfs() {  //在惨量网络中构造分层图 
    for(register int i=1;i<=n;i++) dis[i]=inf;
    queue<int> q;
    q.push(s);
    dis[s]=0;
    now[s]=head[s];
    while(!q.empty()) {
        int x=q.front();
        q.pop();
        for(register int i=head[x];i;i=e[i].net) {
            int v=e[i].to;
            if(e[i].val>0&&dis[v]==inf) {
                q.push(v);
                now[v]=head[v];
                dis[v]=dis[x]+1;
                if(v==t) return 1;
            }
        }
    }
    return 0;
}

inline int dfs(int x,long long sum) {  //sum是整条增广路对最大流的贡献
    if(x==t) return sum;
    long long k,res=0;  //k是当前最小的剩余量 
    for(register int i=now[x];i&&sum;i=e[i].net) {
        now[x]=i;  //当前弧优化 
        int v=e[i].to;
        if(e[i].val>0&&(dis[v]==dis[x]+1)) {
            k=dfs(v,min(sum,e[i].val));
            if(k==0) dis[v]=inf;  //剪枝,去掉增广完毕的点 
            e[i].val-=k;
            e[i^1].val+=k;
            res+=k;
            sum-=k;
        }
    }
    return res;
}

int main() {
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(register int i=1;i<=m;i++) {
        scanf("%d%d%lld",&u,&v,&w);
        add(u,v,w);
    }
    while(bfs()) {
        ans+=dfs(s,inf);
    }
    printf("%lld",ans);
    return 0;
}

 

posted @ 2020-07-09 20:22  5656566  阅读(162)  评论(0)    收藏  举报