zoukankan      html  css  js  c++  java
  • 学习笔记——网络流

    本文仅仅记录做法或代码,至于标准证明等并不会出现(反正也是给自己看的qwq)

    网络最大流

    bool bfs(int s,int t)
    {
        for(int i=1;i<=n;++i) cur[i]=head[i];
        memset(dep,0,sizeof(dep));
        queue<int> q;
        q.push(s); dep[s]=1;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=head[u];i;i=edge[i].next)
            {
                int v=edge[i].to;
                if(!dep[v]&&edge[i].flow)
                {
                    dep[v]=dep[u]+1;
                    q.push(v);
                    if(v==t) return true;
                }
            }
        }
        return false;
    }
    int dfs(int s,int t,int u,int rest)
    {
        if(u==t) return rest;
        int used=0;
        for(int i=cur[u];i;i=edge[i].next)
        {
            cur[u]=i;
            int v=edge[i].to;
            if(dep[v]==dep[u]+1&&edge[i].flow)
            {
                int k=dfs(s,t,v,min(edge[i].flow,rest-used));
                if(!k) dep[v]=0;
                used+=k;
                edge[i].flow-=k;
                edge[i^1].flow+=k;
            }
        }
        return used;
    }
    int dinic(int s,int t)
    {
        int maxflow=0,flow;
        while(bfs(s,t)) while(flow=dfs(s,t,s,INF)) maxflow+=flow;
        return maxflow; 
    }
    

    最小费用最大流

    仅仅只是将bfs变为spfa,但是将原来的多路增广又打回单路增广

    bool Spfa(int s,int t)
    {
        memset(dis,100,sizeof(dis));
        memset(flow,100,sizeof(flow));
        memset(exist,0,sizeof(exist));
        queue<int> q;
        pre[t]=-1; exist[s]=1; dis[s]=0; q.push(s);
        while(!q.empty())
        {
            int u=q.front();q.pop();exist[u]=0;
            for(int i=head[u];i;i=edge[i].next)
            {
                int v=edge[i].to;
                if(edge[i].flow>0&&dis[v]>dis[u]+edge[i].dis)
                {
                    dis[v]=dis[u]+edge[i].dis;
                    flow[v]=min(edge[i].flow,flow[u]);
                    pre[v]=u;
                    preedge[v]=i;
                    if(!exist[v])
                    {
                        exist[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    void MCMF()
    {
        while(Spfa(s,t))
        {
            maxflow+=flow[t];
            mincost+=flow[t]*dis[t];
            int now=t;
            while(now!=s)
            {
                edge[preedge[now]].flow-=flow[t];
                edge[preedge[now]^1].flow+=flow[t];
                now=pre[now];
            }
        }
    }
    

    无源汇上下界可行流

    需要保证每个点都有入度和出度才可以回环往复保证流量守恒

    先将每条边流量初始赋值为下界,然后问题就是有些点不遵从流量守恒

    解决方案:

    建立超级源汇点S,T,对于入流量大于出流量的点i,为了使入流量=出流量,我们将S给i连一条容量为(入-出)的边,从而在不改变原图流入量的同时通过引入外资增加流出量;对于入流量小于出流量的点i,同理用i连T一条容量为(出-入)的边(可以理解为通过这条边引导入流量增加,然后删掉这个不存在的边原图就守恒了)

    连边(d=入-出)

    for(int i=1;i<=n;++i)
        {
            if(d[i]>0) ad(S,i,d[i]);
            if(d[i]<0) ad(i,T,-d[i]);
        }
    

    连完边后跑最大流即可,如果从S出发的边全部满流则为有可行流,原图中每条边的反边的剩余流量即为可行流


    有源汇上下界可行流

    除了源点和汇点流量不守恒外其余点依旧守恒,然而源点出流和汇点入流相等,所以将t和s用容量INF的边连起来之后就成无源汇上下界可行流


    上下界最大流

    用无源汇上下界可行流的方法连边后从S跑最大流,得到flow1(可行流),删去S,T和其所连边,从s再跑一次最大流得到flow2(剩余流量),答案为flow1+flow2


    上下界最小流

    用上面方法得到可行流flow1之后(就可以不要了),用t向s连一条INF的边(注意是小写),然后再跑一次最大流,之前连的边的反边即为答案

        dinic(S,T);
        ad(t,s,INF);
        dinic(S,T);
        cout<<edge[cnt].flow<<endl;
    

    上下界最小费用流

    连边方法同上下界最小流,将求最大流改为求最小费用最大流

    答案为每条边的下界*单价+最小费用流

    AHOI2014/JSOI2014支线剧情

    求从1出发的多条路径覆盖所有边至少一次的最小代价

        read(n);
        s=1;t=n+1;S=n+2;T=n+3;
        for(int i=1;i<=n;++i)
        {
            read(m);
            while(m--)
            {
                int x,t; read(x);read(t);
                //单价为t
                ins(i,x,1,INF,t);
                ans+=t;//下界为1,所以只加t*1
            }
        }
        for(int i=1;i<=n+1;++i)
        {
            if(d[i]>0) ad(S,i,d[i],0);
            if(d[i]<0) ad(i,T,-d[i],0);
        }
        ad(t,s,INF,0);//连边跑费用流
        ans+=MCMF(S,T);
        cout<<ans<<endl;
    
  • 相关阅读:
    通过java代码获取jvm信息和系统信息
    java cp与java jar的区别
    vue下实现WebRTC
    MANIFEST.MF文件详解
    element 前端排序 与 后端排序
    JAVA获取CPUID、主板序列号、硬盘序列号、MAC地址(自己验证过)
    PHP常用代码大全
    程序员从初级到中级10个秘诀
    移动平台还有哪些创业机会
    程序员招聘:如何识别真正的程序员
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11008265.html
Copyright © 2011-2022 走看看