网络流初步

Posted waterflower

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流初步相关的知识,希望对你有一定的参考价值。

其实网络流很久之前已经学过,但是因为一些原因搁置了很久,于是想再系统地复习一下.

由于博主能力有限,所以关于网络流知识也是了解个大概,这里只是简单介绍,并且说一下博主的感性理解


 

最大流

  EK増广路算法

    很容易理解的一个算法,也就是我们不断地bfs找出一条増广路然后更新剩余容量,直到更新完毕,类似于SPFA做法.时间复杂度$O(nm^2)$;

    这里不再附上代码,因为后面的费用流就要用EK+SPFA,而只是求最大流,推荐Dinic.

  Dinic算法

    与EK不同的是,Dinic算法增加了一些优化,这里引进了深度这个概念,通过在同一深度图中増广,一个点可以向多个点进行多流増广,并有减枝;

    于是Dinic就可以达到$O(n^2m)$的优秀时间复杂度,可以代替匈牙利算法跑二分图匹配,时间复杂度$O(n\sqrtn)$;

    PS:关于二分图匹配时间复杂度可以这么想,由于一个点多流推进,同时増广多个点,而且深度小,所以时间复杂度就十分优秀;

    Code:

    

#include<bits/stdc++.h>
#define maxn 10008
using namespace std;
int n,m,head[maxn],s,t,cent=1,d[maxn],maxflow;
int min(int a,int b)return a<b?a:b;
const int inf=1<<30;
struct node
    int next,to,w;
edge[maxn<<5];
queue<int >q;

void add(int u,int v,int w)
    edge[++cent]=(node)head[u],v,w;head[u]=cent;
    edge[++cent]=(node)head[v],u,0;head[v]=cent;


bool bfs()
    memset(d,0,sizeof d);
    while(q.size()) q.pop();
    q.push(s),d[s]=1;
    while(!q.empty())
        int x=q.front();q.pop();
        for(int i=head[x];i;i=edge[i].next)
            int y=edge[i].to;
            if(edge[i].w&&!d[y])
                q.push(y);d[y]=d[x]+1;
                if(y==t) return 1;
            
        
    
    return 0;


int Dinic(int x,int flow)
    if(x==t) return flow;
    int rest=flow,k,y;
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].w&&d[y=edge[i].to]==d[x]+1)
            k=Dinic(y,min(rest,edge[i].w));
            edge[i].w-=k;
            edge[i^1].w+=k;
            rest-=k;
        
    
    return flow-rest;


int main()
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1,a,b,w;i<=m;i++)
        scanf("%d%d%d",&a,&b,&w);
        add(a,b,w);
    
    int flow=0;
    while(bfs())
        while(flow=Dinic(s,inf)) maxflow+=flow;
    printf("%d",maxflow);

费用流

  费用流应该是我们见得最多的,而费用流跑二分图最大带权匹配极其优秀,费用流采用EK算法,不过每一次要跑一次SPFA,时间复杂度上升,如果要卡图了话,可能会崩;

  但是由于图一般比较小,而且Dijkstra处理负权值问题有些复杂(要牵扯到势的辅助),所以还是普遍用SPFA求费用流;

  Code

  

#include<bits/stdc++.h>
#define maxn 50007
#define N 5007
#define inf 2139062143
using namespace std;
int n,m,s,t,incf[N],head[N],cent=1,dis[N];
int vis[N],maxflow,mincost,pre[N];
struct node
    int next,to,w,cost;
edge[maxn<<3];

inline void add(int u,int v,int w,int c)
    edge[++cent]=(node)head[u],v,w,c;head[u]=cent;
    edge[++cent]=(node)head[v],u,0,-c;head[v]=cent;


bool spfa()
    queue<int >q;
    memset(pre,0,sizeof(pre)); 
    memset(dis,127,sizeof dis);
    memset(vis,0,sizeof vis);
    q.push(s);dis[s]=0;vis[s]=1;
    incf[s]=1<<30;
    while(!q.empty())
        int x=q.front();q.pop();
        vis[x]=0;
        for(int i=head[x],y;i;i=edge[i].next)
            if(!edge[i].w) continue;
            if(dis[y=edge[i].to]>dis[x]+edge[i].cost)
                dis[y]=dis[x]+edge[i].cost;
                incf[y]=min(incf[x],edge[i].w);
                pre[y]=i;
                if(!vis[y]) q.push(y),vis[y]=1;
            
        
    
    if(dis[t]==2139062143) return 0;
    return 1;


void update()
    int x=t;
    while(x!=s)
        int i=pre[x];
        edge[i].w-=incf[t];
        edge[i^1].w+=incf[t];
        x=edge[i^1].to;
    
    maxflow+=incf[t];
    mincost+=dis[t]*incf[t];
//    cerr<<mincost<<endl;


int main()
//    freopen("cin.in","r",stdin);
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1,a,b,c,d;i<=m;i++)
        scanf("%d%d%d%d",&a,&b,&c,&d);
        add(a,b,c,d);
    
    while(spfa()) update();
    printf("%d %d",maxflow,mincost);

 

以上是关于网络流初步的主要内容,如果未能解决你的问题,请参考以下文章

网络流初步

网络流初步

网络流初步

初步网络最大流的算法及详解

网络流初步

网络流初步