zoukankan      html  css  js  c++  java
  • 网络流

    网络流=带反悔的贪心。——517

    个人认为网络流=最大流dinic/费用流板子+玄学意会建图。

    网络流朴素算法ek

    对于每条边 ((u,v,w)) ,建一条相应的反向边 ((v,u,0))
    算法执行时,先从源点s bfs,看看到t最多能流多少,对于每个节点记录它的前驱节点,如果到t的流量不为0,那么从t回溯回s,将每条边的容量减去流量,其反向边的容量加上流量,然后把答案加上所有回溯到的边的流量;否则停止执行,返回结果。最坏复杂度 (O(nm^2))

    #include<bits/stdc++.h>
    #define maxn 10005
    #define maxm 100005
    #define INF 1050000000
    using namespace std;
    template<typename tp>
    void read(tp& x){
        x=0;
        char c=getchar();
        bool sgn=0;
        while((c<'0'||c>'9')&&c!='-')c=getchar();
        if(c=='-')sgn=1,c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
        if(sgn)x=-x;
    }
    template<typename tp>
    void write(tp x){
        if(x<0)putchar('-'),write(-x);
        else{
            if(x>=10)write(x/10);
            putchar(x%10+'0');
        }
    }
    struct edge{
        int to,next,w;
    }e[maxm<<1];
    int head[maxn],cnte;
    void add(int u,int v,int w){
        e[++cnte].to=v;
        e[cnte].w=w;
        e[cnte].next=head[u];
        head[u]=cnte;
    }
    int n,m,s,t,pre[maxn],edg[maxn],flow[maxn];
    bool bfs(){
        for(int i=1;i<=n;i++)pre[i]=edg[i]=-1,flow[i]=INF;
        pre[s]=0;
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(u==t)break;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].to;
                if(e[i].w>0&&pre[v]==-1){
                    pre[v]=u;
                    edg[v]=i;
                    flow[v]=min(flow[u],e[i].w);
                    q.push(v);
                }
            }
        }
        return pre[t]!=-1;
    }
    int ek(){
        int ans=0;
        while(bfs()){
            int x=t;
            while(x!=s){
                e[edg[x]].w-=flow[t];
                e[edg[x]^1].w+=flow[t];
                x=pre[x];
            }
            ans+=flow[t];
        }
        return ans;
    }
    signed main(){
        read(n),read(m),read(s),read(t);
        for(int i=1;i<=n;i++)head[i]=-1;
        cnte=-1;
        for(int i=1;i<=m;i++){
            int u,v,w;
            read(u);read(v);read(w);
            add(u,v,w);
            add(v,u,0);
        }
        write(ek());
        return 0;
    }
    

    网络流进阶算法dinic

    解释不来,请上网搜索……
    最坏复杂度 (O(n^2m))
    head1[]:当前弧优化,极小部分题会卡,如LOJ117,加了快10倍

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=10003,maxm=200003,INF=1050000000;
    struct edge{int to,next,w;}e[maxm<<1];
    int head[maxn],head1[maxn],cnte;
    void add(int u,int v,int w){e[++cnte].to=v,e[cnte].w=w,e[cnte].next=head[u],head[u]=cnte;}
    void addedge(int u,int v,int w){add(u,v,w),add(v,u,0);}
    int n,s,t,dep[maxn],q[maxn];
    bool bfs(){
        for(int i=1;i<=n;i++)dep[i]=0,head1[i]=head[i];
        dep[s]=1;
        int *qhead=q,*qtail=q;
        *qtail++=s;
        while(qhead!=qtail){
            int u=*qhead++;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].to;
                if(e[i].w&&dep[v]==0){
                    dep[v]=dep[u]+1;
                    *qtail++=v;
                }
            }
        }
        return dep[t]!=0;
    }
    int dfs(int u,int low){
        if(low==0||u==t)return low;
        int flow=0;
        for(int &i=head1[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].w&&dep[v]==dep[u]+1){
                int tmp=dfs(v,min(low,e[i].w));
                if(tmp==0)dep[v]=0;
                else{
                    flow+=tmp;
                    low-=tmp;
                    e[i].w-=tmp;
                    e[i^1].w+=tmp;
                    if(low==0)break;
                }
            }
        }
        return flow;
    }
    int dinic(){
        int ans=0;
        while(bfs())ans+=dfs(s,INF);
        return ans;
    }
    int main(){
        int m;
        scanf("%d%d%d%d",&n,&m,&s,&t);
        for(int i=1;i<=n;i++)head[i]=-1;
        cnte=-1;
        while(m--){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        printf("%d",dinic());
        return 0;
    }
    

    费用流朴素算法spfa+ek

    对于每条边 ((u,v,w,cost)) ,建反向边 ((v,u,w,-cost))
    该算法即在最大流ek的基础上把bfs改为spfa。为什么不能直接用dijkstra?因为有负环。

    #include<bits/stdc++.h>
    #define maxn 5005
    #define maxm 100005
    #define INF 1050000000
    using namespace std;
    struct edge{int to,next,w,cost;}e[maxm<<1];
    int head[maxn],cnte;
    void add(int u,int v,int w,int cost){e[++cnte].to=v,e[cnte].w=w,e[cnte].cost=cost,e[cnte].next=head[u],head[u]=cnte;}
    void addedge(int u,int v,int w,int cost){add(u,v,w,cost),add(v,u,w,-cost);}
    int n,s,t,pre[maxn],edg[maxn],flow[maxn],maxflow,dis[maxn],mincost;
    bool vis[maxn];
    bool spfa(){
        for(int i=1;i<=n;i++)flow[i]=dis[i]=INF,vis[i]=0;
        pre[t]=-1;
        dis[s]=0;
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            vis[u]=0;
            for(int i=head[u];~i;i=e[i].next){
                int v=e[i].to;
                if(e[i].w>0&&dis[u]+e[i].cost<dis[v]){
                    dis[v]=dis[u]+e[i].cost;
                    pre[v]=u;
                    edg[v]=i;
                    flow[v]=min(flow[u],e[i].w);
                    if(!vis[v]){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    void micmxf(){
        while(spfa()){
            int x=t;
            while(x!=s){
                e[edg[x]].w-=flow[t];
                e[edg[x]^1].w+=flow[t];
                x=pre[x];
            }
            maxflow+=flow[t];
            mincost+=flow[t]*dis[t];
        }
    }
    signed main(){
        int m;
        scanf("%d%d%d%d",&n,&m,&s,&t);
        for(int i=0;i<=n;i++)head[i]=-1;
        cnte=-1;
        for(int i=1;i<=m;i++){
            int u,v,w,cost;
            scanf("%d%d%d%d",&u,&v,&w,&cost);
            addedge(u,v,w,cost);
        }
        micmxf();
        printf("%d %d
    ",maxflow,mincost);
        return 0;
    }
    

    网络流常用建图方法

    BZOJ1001

    最朴素的建模。
    直接在原图上源点 (s=1) ,汇点 (t=n*m) 跑最大流即可。

    网络流24题 飞行员配对方案问题

    理解最大流和二分图匹配的关系。
    本题构造一个二分图,对于所有左侧点 (i) ,连边 ((s,i,INF)) ;对于所有右侧点 (j) ,连边 ((j,t,INF)) ,对于二分图的每条边,连边 ((u,v,1))

    网络流24题 最小路径覆盖问题

    最小路径覆盖。
    先将每个点 (u) 拆成一个入点 (u_1) 和一个出点 (u_2)
    ((s,i_1,1),(i_2,t,1),(u_2,v_1,1),(i_1,i_2,1))
    求最大流。

    NOI2006 最大获利

    对于每个物品有一个收益,选一些物品要付出一个代价,求最大收益的题目往往用最小割解决。
    本题中,设 (i) 为中转站, ((a_j,b_j,c_j)) 为用户需求,连边 ((s,i,p_i),(j,t,c_j),(a_j,b_j,INF)) ,然后求最大流(最小割),答案为所有收益的和-最小割。可以发现最小割=最小成本+舍弃的收益。

    网络流24题 方格取数问题

    也用最小割解决。
    先对图黑白染色,然后设 (i) 为黑点, (j) 为白点, (k) 为与 (i) 相邻的点,连边 ((s,i,a_i),(j,t,a_j),(i,k,INF))

    CQOI2009 跳舞

    先考虑如何验证给定的舞曲数目是否有解。
    构造最大流,把每个人拆成两个点,男孩 (i_1,i_2) ,女孩 (i_3,i_4) ,连边 ((s,i_1,INF),(i_1,i_4,1),(i_1,i_2,k),(i_2,i_3,1),(i_3,i_4,k),(i_4,t,INF)) ,如果最大流=舞曲数目*人数,那么可行,否则不可行。
    然后我们只需要二分舞曲数目。

    TJOI2015 线性代数

    经过推算, (D=sum_{i=1}^n sum_{j=1}^n a_i*a_j*b_{i,j}-sum_{i=1}^n a_i*c_i)
    这和上面讲的最小割类似,故用最小割解决。
    连边 ((s,i,c_i),(i,(i,j),INF),(j,(i,j),INF),((i,j),t,b_{i,j}))

    CQOI2012 交换棋子

    费用流。
    首先把每一个点拆成三个点 (i_1,i_2,i_3)
    (j) 是与 (i) 八连通的点,若 (i) 初始状态为1,连边 ((s,i_2,1,0)) ;若 (i) 末状态为1,连边 ((i_2,t,1,0))
    对于所有节点,连边 ((i_1,i_2,w_1,0),(i_2,i_3,w_2,1),(i_3,j_1,INF,0)) ,其中若 (m_{i,j}) 为奇数且 ((i,j)) 初状态为1,末状态为0, (w_1=lfloor frac{m}{2} floor,w_2=lfloor frac{m+1}{2} floor) ;若 (m_{i,j}) 为奇数且 ((i,j)) 初状态为0,末状态为1, (w_1=lfloor frac{m+1}{2} floor,w_2=lfloor frac{m}{2} floor) ;否则 (w_1=frac{m}{2},w_2=frac{m}{2})

  • 相关阅读:
    node基础(2)
    node基础知识(1)
    面试题1
    HTML5中的表单属性
    H5中元素的拖放
    Niagara基于javascript的控件开发
    CSS居中
    2018-3-21 url 、href、src
    2018-3-7 色彩的一些知识
    JS基本数据类型
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/11178210.html
Copyright © 2011-2022 走看看