码迷,mamicode.com
首页 > 其他好文 > 详细

网络流初步

时间:2019-08-17 18:18:07      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:space   for   add   简单   容量   知识   ++   define   匹配   

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

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


 

最大流

  EK増广路算法

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

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

  Dinic算法

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

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

    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);
}

 

网络流初步

标签:space   for   add   简单   容量   知识   ++   define   匹配   

原文地址:https://www.cnblogs.com/waterflower/p/11369337.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!