洛谷 P3376 【模板】网络最大流

来源:互联网 发布:单片机vs1003做mp3 编辑:程序博客网 时间:2024/06/07 05:22

 洛谷 P3376 【模板】网络最大流

//P3376 【模板】网络最大流

提供三种算法:Dinic算法 Ford-Fulkerson算法Ford-Fulkerson算法

代码都没有经过优化,浅显易懂,全部用C语言实现。

可以供初学者参考。2017-7-1 18:28

//P3376 【模板】网络最大流
//dinic算法研究
//http://blog.csdn.net/wall_f/article/details/8207595代码写得比较清晰
//http://blog.csdn.net/Clove_unique/article/details/50683086此文代码写得比较完整
//关于dinic算法,此文介绍得相当棒https://comzyh.com/blog/archives/568/
//对初学者来说,代码的可读性比效率更重要。入门比精通更重要。
//经过磕磕碰碰的代码修改,样例通过。
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
    int to,next,c;
}e[100000*2+100];
int n,m,s,t,head[10000+100],q[10000+100],cnt=1,maxflow=0,level[10000+100];
int min(int a,int b){
    return a>b?b:a;
}
int bfs(int s1,int t1){//分层图
    int u,v,h,t,b;//b边
    memset(level,0,sizeof(level));
    level[s1]=1;
    h=t=1;//2 漏了该句
    q[t]=s1;
    t++;
    while(h<t){
        u=q[h];
        for(b=head[u];b>1;b=e[b].next){//1 此处写成 for(b=head[u];b;b=e[b].next)
            v=e[b].to;
            if(!level[v]&&e[b].c){//没有设置过层次,同时有允许通过的流量
                level[v]=level[u]+1;
                q[t]=v;
                t++;
            }
        }
        h++;
    }
    return level[t1]>0;//level[t1]==0分层图不成功,level[t1]>0分层图成功
}
int dfs(int now,int t1,int limit){//增广路径
    int u,v,b,flow=0,f;
    if(!limit||now==t1)return limit;//传递流量为0,或者已经到达终点
    for(b=head[now];b>1;b=e[b].next){//1 此处写成 for(b=head[now];b;b=e[b].next)
        v=e[b].to;
        if(level[now]+1==level[v]&&(f=dfs(v,t1,min(e[b].c,limit)))){// 满足层次关系,且剩余流量不为0
            flow+=f;
            limit-=f;
            e[b].c-=f;//正向边
            e[b^1].c+=f;//反向边
            if(!limit)break;//剩余流量为0
        }
    }
    return flow;
}
void dinic(int s1,int t1){
    while(bfs(s1,t1)){
        maxflow+=dfs(s1,t1,INF);
    }
}
void add(int u,int v,int c){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt,e[cnt].c=c;//3 漏写了 e[cnt].c=c
}
int main(){
    int i,u,v,c;
    memset(head,0,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c);//正向边
        add(v,u,0);//反向边
    }
    dinic(s,t);
    printf("%d\n",maxflow);
    return 0;
}

2017-7-1 18:26


//P3376 【模板】网络最大流
//http://blog.csdn.net/smartxxyx/article/details/9293665/此文原理介绍得不错
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P3376此文代码写得不错,摘抄如下:
//网络流的模板题,使用朴素的Ford-Fulkerson算法,一次增广一条路径,并更新残余网络。设求解的最大流为F,
//则它最多经过F次增广,其复杂度为O(F|E|),不过这个上界很松,所以实际效率还是比较快的
//思路比较像高中物理中的电流,与物理竞赛中涉及的基尔霍夫定律还是有异曲同工之妙。
//提交,测试点2,4-8,10WA,测试点9TLE 静态排查:vis[s]=1;//2 漏了该句,这是没想法了
//提交,测试点2,9TLE,测试点5-8,10WA,经过排查,发现找正向边,反向边的算法不对 cnt=0 e[b],e[b+1]不对
//注意,找正向边,反向边,要有技巧,cnt=1,e[b],e[b^1];cnt++,从2开始
/* 注意,因为后面的位运算找回边的算法,
正向边和反向边必须是“0,1”、“2,3”这样的数,
因为它们的二进制除了最后一位都一样,
这样异或1(最后一位取反)就能找到对方。
*/
//修改,提交90分,测试点9TLE,该题使用 Ford-Fulkerson算法,到头了,90分结束,要学习 EdmondsKarp算法
//http://blog.sina.com.cn/s/blog_6cf509db0100uy5n.html此文介绍网络流之最大流算法(EdmondsKarp)
//http://www.cnblogs.com/zsboy/archive/2013/01/27/2878810.html也介绍得不错。
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P3376介绍得不错,摘抄如下:
//区别在于EK用bfs找增广路,而FF用dfs,所以FF很容易TLE
//http://www.cnblogs.com/hxsyl/p/4185530.html该文代码注释写得不错。
//样例通过,提交,测试点2,9,10RE,静态排查,发现 e[100000*2+100];//2 此处写成 e[100000+100]修改,提交AC
//2017-6-29 18:47
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
    int from,to,next,c,flow;
}e[100000*2+100];//2 此处写成 e[100000+100]
int n,m,s,t,a[10000+100],head[10000+100],q[10000+100],cnt=1,pre[10000+100],flow=0;//a[]当前节点对应最小流量
void add(int u,int v,int c){
    cnt++,e[cnt].from=u,e[cnt].to=v,e[cnt].c=c,e[cnt].next=head[u],head[u]=cnt,e[cnt].flow=0;
}
int min(int a,int b){
    return a>b?b:a;
}
void bfs(int s1,int t1){
    int h,tail,b,u,v;
    while(1){
        memset(a,0,sizeof(a));
        h=tail=1;
        q[tail]=s1;
        tail++;
        a[s1]=INF;
        while(h<tail){
            u=q[h];
            for(b=head[u];b>1;b=e[b].next){
                v=e[b].to;
                if(!a[v]&&e[b].c>e[b].flow){
                    a[v]=min(a[u],e[b].c-e[b].flow);
                    q[tail]=v;
                    pre[v]=b;
                    tail++;
                }
            }
            if(a[t])break;//到了t点//1 摆错位置,摆到之前for的if中
            h++;
        }
        if(!a[t])break;//到不了t点
        for(u=t;u!=s;u=e[pre[u]].from){
            e[pre[u]].flow+=a[t];//反向边
            e[pre[u]^1].flow-=a[t];//正向边
        }
        flow+=a[t];
    }
}
int main(){
    int i,u,v,c;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c);
        add(v,u,0);
    }
    bfs(s,t);
    printf("%d\n",flow);
    return 0;
}




//http://blog.csdn.net/smartxxyx/article/details/9293665/此文原理介绍得不错
//https://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P3376此文代码写得不错,摘抄如下:
//网络流的模板题,使用朴素的Ford-Fulkerson算法,一次增广一条路径,并更新残余网络。设求解的最大流为F,
//则它最多经过F次增广,其复杂度为O(F|E|),不过这个上界很松,所以实际效率还是比较快的
//思路比较像高中物理中的电流,与物理竞赛中涉及的基尔霍夫定律还是有异曲同工之妙。
//提交,测试点2,4-8,10WA,测试点9TLE 静态排查:vis[s]=1;//2 漏了该句,这是没想法了
//提交,测试点2,9TLE,测试点5-8,10WA,经过排查,发现找正向边,反向边的算法不对 cnt=0 e[b],e[b+1]不对
//注意,找正向边,反向边,要有技巧,cnt=1,e[b],e[b^1];cnt++,从2开始
/* 注意,因为后面的位运算找回边的算法,
正向边和反向边必须是“0,1”、“2,3”这样的数,
因为它们的二进制除了最后一位都一样,
这样异或1(最后一位取反)就能找到对方。
*/
//修改,提交90分,测试点9TLE,该题使用 Ford-Fulkerson算法,到头了,90分结束,要学习 EdmondsKarp算法
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
    int to,next,c;
}e[100000*2+100];
int n,m,s,t,cnt=1,head[10000+100],vis[10000+100],ans=0;//3 此处写成cnt=0
int min(int a,int b){
    return a>b?b:a;
}
void add(int u,int v,int c){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],e[cnt].c=c,head[u]=cnt;
}
int dfs(int u,int flow){
    int v,b,delta;//b边
    if(u==t){
        ans+=flow;
        return flow;
    }
    for(b=head[u];b>1;b=e[b].next){
        v=e[b].to;
        if(!vis[v]&&e[b].c>0){
            vis[v]=1;
            delta=dfs(v,min(e[b].c,flow));
            if(delta>0){
                e[b].c-=delta;
                e[b^1].c+=delta;//3 此处写成 e[b+1].c+=delta;
                return delta;
            }
        }
    }
    return 0;//1 漏了该句,最致命的
}
int main(){
    int u,v,c,i,j;
    memset(head,0,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c);//正向边
        add(v,u,0);//反向边
    }
    while(1){
        memset(vis,0,sizeof(vis));
        vis[s]=1;//2 漏了该句,这是没想法了
        if(!dfs(s,INF))
            break;
    }
    printf("%d\n",ans);
    return 0;
}


原创粉丝点击