网络流初步最大流(EK算法和Dinic算法进阶)

网络流最大流(network_flows)

网络流是一种类比水流的解决问题的方法,首先我们要明白它解决的是什么样的问题。
比如说最基本的,从水厂通过各种水管到达你家的能有多少水量,每个水管有自己的流量限制,也就是说最多留这么多水。

从1到达3,结果为3。水可以走许多条路,但是最终还是不会大于2到3管道的1,4到3管道的2流量。
在这里只介绍简单的EK算法,解决一般的网络流问题足够用,dinic算法明白其中的含义会用就行,不需要深究。

/*
1.网络流算法用来一个网络中的最大流量,每个水管都有自己的某个限制。
2.其实简单的ek算法就是通过不断寻找增广路,找到增广路中的最小权值然后减去它。
3.记录了前驱,然后倒着走一遍,将所有网络中的边减去这个最小值,再次寻找增广路。
4.增广路其实找一条能到的路就行。
5.这个EK算法可以寻找,复杂度o(v*e*e)。DINIC算法又加上了个dfs,复杂度有所减少。
我的网络流模板出了点问题,这里借用了大佬的模板,看起来比较详细(orz)。
*/
/*测试样例
5 4
1 2 5
1 4 5
2 3 1
4 3 2
2 4 3
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
const int inf=0x7fffffff;
int r[maxn][maxn]; //残留网络,初始化为原图
bool visit[maxn];
int pre[maxn];
int m,n;
bool bfs(int s,int t){
 //寻找一条从s到t的增广路,若找到返回true,所谓增广路其实就是找s到t的一条路,能找到就行,并记录前驱。
    int p;
    queue<int > q;
    memset(pre,-1,sizeof(pre));
    memset(visit,false,sizeof(visit));
    pre[s]=s; visit[s]=true; q.push(s);
    //从s到t
    while(!q.empty()){         /这里用临界矩阵存储边,更好的可以用前向星。
        //该过程其实就是爆搜。
        p=q.front();
        q.pop();
        for(int 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;
}
int EdmondsKarp(int s,int t){
   int flow=0,d,i;
   /*
     它的思想,如果有增广路(能到的路),因为我记录了前驱所以我可以对这条路
     倒着走一遍,找到这些边权值最小的那个,然后然所有边的流量减去这个最小值
     就行。这样就保证了所有流量
   */
   while(bfs(s,t)){
       d=inf;
       for(i=t;i!=s;i=pre[i])   d=d<r[pre[i]][i]? 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(){
    while(scanf("%d%d",&m,&n)!=EOF){
        int u,v,w;
        memset(r,0,sizeof(r));///
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&u,&v,&w);
            r[u][v]+=w;
        }
        printf("%d\n",EdmondsKarp(1,3));//可以计算1到x的最大流量。
    }
    return 0;
}

对上数样例输出为:3 结果显然。
学习网络流需要掌握EK算法的经典思想,dinic算法只给出模板,理解其中含义即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
#define MAX 10100
#define MAXL 210000
#define rg register
#define INF 1000000000
inline int read()  //读入优化
{
       rg int x=0,t=1;rg char ch=getchar();
       while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
       if(ch=='-'){t=-1;ch=getchar();}
       while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
       return x*t;
}
int N,M,S,T,Ans=0;
bool vis[MAX];
struct Line
{
       int v,next,w,fb;
}e[MAXL];
int h[MAX],cnt=1;
int level[MAX];
inline void Add(rg int u,rg int v,rg int w)
{
        e[cnt]=(Line){v,h[u],w,cnt+1};
        h[u]=cnt++;
        e[cnt]=(Line){u,h[v],0,cnt-1};//存反边
        h[v]=cnt++;
}
inline bool BFS()//分层
{
        for(int i=1;i<=N;++i)level[i]=0;
        level[S]=1;
        queue<int> Q;
        while(!Q.empty())Q.pop();
        Q.push(S);
        while(!Q.empty())
        {
               int u=Q.front();Q.pop();
               for(int i=h[u];i;i=e[i].next)
               {
                        int v=e[i].v;
                        if(e[i].w&&!level[v])//可以增广
                        {
                               level[v]=level[u]+1;
                               Q.push(v);
                        }
               }
        }
        return level[T];//返回是否存在增广路
}
int DFS(rg int u,rg int t,rg int f)//从u到t,当前流量f
{
        if(u==t||f==0)return f;//到达终点或者已经无法增广了
        int re=0;//返回值
        for(int i=h[u];i;i=e[i].next)//访问所有边
        {
                rg int v=e[i].v,d;
                if(e[i].w&&level[v]==level[u]+1)//可以增广,并且满足分层图的要求
                {
                       d=DFS(v,T,min(f,e[i].w));
                       re+=d;
                       f-=d;
                       e[i].w-=d;//更新流量
                       e[e[i].fb].w+=d;//反边
                }
        }
        return re;
}
inline int Dinic()
{
        int re=0;
        while(BFS())re+=DFS(S,T,INF);
        return re;
}
int main()
{
        N=read();M=read();S=read();T=read();
        for(rg int i=1;i<=M;++i)
        {
               rg int u=read(),v=read(),w=read();
               Add(u,v,w);
        }
        rg int Ans=Dinic();
        printf("%d\n",Ans);
        return 0;
}

***五一快乐 想看老师女装 ***

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值