网络流模板

网络流模板

用最通俗的语言让你学会网络流

%%%大佬的学习笔记

网络单纯形——传说最快费用流

技巧

  1. $ \begin{array}{c}
    \left | f+f' \right | = \left | f \right | +\left | f' \right |
    \end{array}$
    所以,当可行流的残余网络有可行流,原网络的可行流一定不是最大流

  2. 对于任何一个可行解,都对应一个可行流 并且 对于任何一个可行流,都对应一个可行解

  3. 如果限制流的距离,分层图

  4. 如果流网络要附加信息,可以考虑乘上一个常数 \(P\),然后再加上附加数据,即\(xP+y\),只要\(P>y\),结果就不会对原有数据造成影响,同时也能求出附加数据的 \(最大/小\) 值,详见这道题


模板

网络最大流

Dinic算法

#include<bits/stdc++.h>
using namespace std;
const int MX_N=5010,MX_M=50100;
const int INF=0x3f3f3f3f;
struct node{
    int to,next,w;
}edge[MX_M<<1];
int head[MX_N]={0},edge_cnt=0;
inline void Add(int x,int y,int w){
    node &i=edge[edge_cnt];
    i.w=w,i.to=y,i.next=head[x];
    head[x]=edge_cnt++;
}
inline void add(int x,int y,int w){
    Add(x,y,w),Add(y,x,0);
}
int s=0,t=MX_N-1;
int cur[MX_N]={0},dist[MX_N]={0};
bool bfs(){
    for(int i=0;i<MX_N;i++)  cur[i]=head[i],dist[i]=-1;
    queue<int > qu;qu.push(s);dist[s]=0;
    while(!qu.empty()){
        int now=qu.front();qu.pop();
        for(int i=head[now];i!=-1;i=edge[i].next){
            int to=edge[i].to;
            if(dist[to]==-1&&edge[i].w){
                dist[to]=dist[now]+1;
                qu.push(to);
            }
        }
    }
    return dist[t]!=-1;
}
int dfs(int now,int flow){
    if(now==t)  return flow;
    int left=flow;
    for(int &i=cur[now];i!=-1;i=edge[i].next){
        int to=edge[i].to,w=edge[i].w;
        if(dist[to]==dist[now]+1&&w){
            int cur_flow=dfs(to,min(left,w));
            left-=cur_flow;
            edge[i].w-=cur_flow;
            edge[i^1].w+=cur_flow;
            if(left==0)  break;
        }
    }
    if(flow==left)  dist[now]=-1;
    return flow-left;
}
int dinic(){
    int sum=0;
    while(bfs()){
        sum+=dfs(s,INF);
    }
    return sum;
}
signed main(){
    memset(head,-1,sizeof(head));
    //=======================================================


    //=======================================================
    return 0;
}

最小费用最大流

EK(Spfa)

#include<bits/stdc++.h>
using namespace std;
const int MX_N=5010,MX_M=50100;
const int INF=0x3f3f3f3f;
struct node{
    int to,next;
    int w,cost;
}edge[MX_M<<1];
int head[MX_N]={0},edge_cnt=0;
inline void Add(int x,int y,int w,int c){
    node& it=edge[edge_cnt];
    it.cost=c;it.next=head[x];it.w=w;it.to=y;
    head[x]=edge_cnt++;
}

inline void add(int x,int y,int w,int c){
    Add(x,y,w,c),Add(y,x,0,-c);
}
int s=0,t=MX_N-1;
int pre[MX_N]={0},lim[MX_N]={0},dist[MX_N]={0};
bool vis[MX_N]={0};
bool spfa(){
    memset(lim,0,sizeof(lim));memset(dist,INF,sizeof(dist));memset(vis,0,sizeof(vis));
    queue<int >qu;
    qu.push(s);lim[s]=INF,vis[s]=1,dist[s]=0;
    while(!qu.empty()){
        int now=qu.front();qu.pop();vis[now]=0;
        for(int i=head[now];~i;i=edge[i].next){
            int to=edge[i].to,w=edge[i].w,cost=edge[i].cost;
            if(w&&dist[to]>dist[now]+cost){
                dist[to]=dist[now]+cost;
                pre[to]=i;
                lim[to]=min(lim[now],w);
                if(!vis[to]){
                    qu.push(to);
                    vis[to]=1;
                }
            }
        }
    }
    return lim[t]>0;
}
void EK(int &flow,int &cost){
    flow=cost=0;
    while(spfa()){
        flow+=lim[t];
        cost+=lim[t]*dist[t];
        for(int i=t;i!=s;i=edge[pre[i]^1].to){
            edge[pre[i]].w-=lim[t];
            edge[pre[i]^1].w+=lim[t];
        }
    }
}
signed main(){
    memset(head,-1,sizeof(head));
    //=======================================

    //=======================================
    return 0;
}

费用流究极无敌超级加倍PLUS版之传说最快最变态费用流——网络单纯形算法

#include<bits/stdc++.h>
using namespace std;
const long long MX_N=5e5+500,MX_M=5e6+500,INF=0x3f3f3f3f3f3f3f3f;
struct node{
    long long from,to,w,cost,next;
} edge[MX_M];
long long head[MX_N]={0},edge_cnt=1;
inline void Add(long long x,long long y,long long w,long long cost){
    edge[++edge_cnt]=(node){x,y,w,cost,head[x]};
    head[x]=edge_cnt;
}
inline void add(long long from,long long to,long long w,long long cost){
    Add(from,to,w,cost);
    Add(to,from,0,-cost);
}

long long piCache[MX_N]={0},fa[MX_N]={0},fe[MX_N]={0},circle[MX_N]={0},mark[MX_N]={0},curtime=1;
// 随机一棵生成树初始化
void init_tree(long long from,long long fi){
    fe[from]=fi;
    fa[from]=edge[fi].from;
    mark[from]=1;
    for(long long i=head[from];i;i=edge[i].next){
        long long to=edge[i].to;
        //不过这里mark就判重用的
        if(mark[to]||edge[i].w==0)continue;
        init_tree(to,i);
    }
}

inline long long pi(long long x){
    if(mark[x]==curtime)return piCache[x];
    mark[x]=curtime;
    piCache[x]=pi(fa[x])+edge[fe[x]].cost;
    return piCache[x];
}

//沿着e在生成树上形成的环推送流
long long pushFlow(long long e){
    //向上标记法求lca,够暴力
    long long rt=edge[e].from,lca=edge[e].to;
    curtime++;
    long long ccnt=0;
    while(rt){
        mark[rt]=curtime;
        rt=fa[rt];
    }
    while(mark[lca]!=curtime){
        mark[lca]=curtime;
        lca=fa[lca];
    }

    //找到被替换的(省域流量最小的)边,记录环上最小剩余流量
    long long minflow=edge[e].w,p=2,del_u=0;
    for(long long from=edge[e].from;from!=lca;from=fa[from]){
        circle[++ccnt]=fe[from];
        if(edge[fe[from]].w<minflow){
            minflow=edge[fe[from]].w;
            del_u=from;
            p=0;
        }
    }
    for(long long from=edge[e].to;from!=lca;from=fa[from]){
        long long ne=fe[from]^1;
        circle[++ccnt]=ne;
        if(edge[ne].w<minflow){
            minflow=edge[ne].w;
            p=1;
            del_u=from;
        }
    }
    circle[++ccnt]=e;

    //沿着环推送流量
    long long cost=0;
    for(long long i=1;i<=ccnt;i++){
        cost+=edge[circle[i]].cost*minflow;
        edge[circle[i]].w-=minflow;
        edge[circle[i]^1].w+=minflow;
    }

    //如果最小的就是当前边就不用加到树上了
    if(p==2)return cost;

    //把边加入树上,翻转一条链
    long long from=edge[e].from,to=edge[e].to;
    if(p==1)swap(from,to);
    long long last_e=e^p,last_u=to;
    while(last_u!=del_u){
        last_e^=1;
        mark[from]--; // 设置这条链上的信息过期
        swap(fe[from],last_e);
        long long nu=fa[from];
        fa[from]=last_u;
        last_u=from;
        from=nu;
    }
    return cost;
}
long long s=0,t=MX_N-1;
//总算法
void simplex(long long &flow,long long &cost){
    flow=cost=0;
    add(t,s,INF,-INF);
    init_tree(t,0);
    mark[t]=++curtime;
    fa[t]=0;
    //反复执行
    bool running=true;
    while(running){
        running=false;
        for(long long i=2;i<=edge_cnt;i++){
            if(edge[i].w&&edge[i].cost+pi(edge[i].from)-pi(edge[i].to)<0){
                cost+=pushFlow(i);
                running=true;
            }
        }
    }
    //flow在每次直接累加是错的,会把正反的都算上,直接根据一开始加的那条边算
    flow=edge[edge_cnt].w;
    cost=cost+flow*INF;
    return ;
}
long long ans[MX_N]={0};
signed main(){
    //=======================================
    long long flow,cost;simplex(flow,cost);       //注意edge_cnt从2开始
    //=======================================
    return 0;
}

无源汇上下界可行流

#include<bits/stdc++.h>
using namespace std;
const int MX_N=5010,MX_M=50100,INF=0x3f3f3f3f;
struct node{
    int next,to,w;
}edge[MX_M<<1];
int head[MX_N]={0},edge_cnt=0;
inline void Add(int x,int y,int w){
    node &i=edge[edge_cnt];
    i.w=w,i.to=y,i.next=head[x];
    head[x]=edge_cnt++;
}
inline void add(int x,int y,int w){
    Add(x,y,w),Add(y,x,0);
}
int s=0,t=MX_N-1;
int cur[MX_N]={0},dist[MX_N]={0};
bool bfs(){
    for(int i=0;i<MX_N;i++)  dist[i]=-1,cur[i]=head[i];
    queue<int >qu;qu.push(s);dist[s]=0;
    while(!qu.empty()){
        int now=qu.front();qu.pop();
        for(int i=head[now];~i;i=edge[i].next){
            int to=edge[i].to,w=edge[i].w;
            if(dist[to]==-1&&w){
                dist[to]=dist[now]+1;
                qu.push(to);
            }
        }
    }
    return dist[t]!=-1;
}
int dfs(int now,int flow){
    if(now==t)  return flow;
    int left=flow;
    for(int &i=cur[now];~i;i=edge[i].next){
        int to=edge[i].to,w=edge[i].w;
        if(dist[to]==dist[now]+1&&w){
            int cur_flow=dfs(to,min(w,left));
            edge[i].w-=cur_flow;
            edge[i^1].w+=cur_flow;
            left-=cur_flow;
            if(left==0)  break;
        }
    }
    if(left==flow)  dist[now]=-1;
    return flow-left;
}
int dinic(){
    int sum=0;
    while(bfs())  sum+=dfs(s,INF);
    return sum;
}
//=============================================
struct no{
    int x,y,dow,upp;
}ed[MX_M<<1];
int ed_cnt=0;
inline void add_ed(int x,int y,int dow,int upp){
    ed[ed_cnt++]={x,y,dow,upp};
}
int in[MX_N]={0};
int build_graph(){
    int sum=0;
    for(int i=0;i<ed_cnt;i++){
        add(ed[i].x,ed[i].y,ed[i].upp-ed[i].dow);
        in[ed[i].x]-=ed[i].dow;
        in[ed[i].y]+=ed[i].dow;
    }
    for(int i=0;i<MX_N;i++){
        if(in[i]<0){
            add(i,t,-in[i]);
        }
        else if(in[i]>0){
            add(s,i,in[i]);sum+=in[i];
        }
    }
    return sum;
}
signed main(){
    memset(head,-1,sizeof(head));
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
        add_ed(a,b,c,d);
    }
    int sum=build_graph();
    if(dinic()!=sum)  return puts("NO"),0;
    printf("YES\n");
    for(int i=0;i<m*2;i+=2){
        printf("%d\n",edge[i^1].w+ed[i/2].dow);
    }
    return 0;
}

有源汇上下界最 大/小 流

#include<bits/stdc++.h> 
using namespace std;
const int MX_M=11000,MX_N=310;
struct node{
    int to,next,w;
}edge[MX_M<<1];
int head[MX_N]={0},edge_cnt=0;
inline void Add(int x,int y,int w){
    node &i=edge[edge_cnt];
    i.w=w,i.next=head[x],i.to=y;
    head[x]=edge_cnt++;
}
inline void add(int x,int y,int w){
    Add(x,y,w),Add(y,x,0);
}
int cur[MX_N]={0},dist[MX_N]={0};
bool bfs(int s,int t){
    memset(dist,-1,sizeof(dist));
    queue<int > qu;qu.push(s);dist[s]=0;
    while (!qu.empty())
    {
        int now=qu.front();qu.pop();
        for(int i=head[now];i!=-1;i=edge[i].next){
            int to=edge[i].to;
            if(dist[to]==-1&&edge[i].w){
                dist[to]=dist[now]+1;
                qu.push(to);
            }
        }
    }
    return dist[t]!=-1;
}
int dfs(int now,int flow,int t){
    if(now==t)  return flow;
    int left=flow;
    for(int &i=cur[now];i!=-1;i=edge[i].next){
        int to=edge[i].to,w=edge[i].w;
        if(dist[to]==dist[now]+1&&w)
        {
            int cur_flow=dfs(to,min(left,w),t);
            left-=cur_flow;
            edge[i].w-=cur_flow;
            edge[i^1].w+=cur_flow;
            if(left==0)  break;
        }
    }
    if(left==flow)  dist[now]=-1;
    return flow-left;
}
int n;
int dinic(int s,int t){
    int ans=0;
    while(bfs(s,t)){
        for(int i=1;i<=n;i++)  cur[i]=head[i];
        ans+=dfs(s,0x3f3f3f3f,t);
    }
    return ans;
}

int flw[MX_N]={0};

signed main(){
    memset(head,-1,sizeof(head));
    int input_n,m,s,t;
    int S,T;int sum=0;

    scanf("%d%d%d%d",&input_n,&m,&s,&t);
    S=input_n+1,T=input_n+2,n=T;

    add(t,s,0x3f3f3f3f);

    for(int i=1;i<=m;i++){
        int a,b,c,d;
        scanf("%d%d%d%d",&a,&b,&c,&d);add(a,b,d-c);
        flw[a]+=c,flw[b]-=c;
    }
    for(int i=1;i<=input_n;i++){
        if(flw[i]<0){
            add(S,i,-flw[i]);sum-=flw[i];
        }
        else{
            add(i,T,flw[i]);
        }
    }
    int sn=dinic(S,T);
    int ans=edge[1].w;
    edge[0].w=0;edge[1].w=0;
    if(sn!=sum){
        printf("No Solution");return 0;
    }
    ans+=dinic(s,t);   //最小流改为 ans-=dinic(s,t); 
    cout<<ans;
    return 0;
}
posted @ 2024-03-01 20:07  是菜菜呀  阅读(51)  评论(0)    收藏  举报