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

    最大流

    dinic算法:每次增广出长度一样的若干条路径,用朴素的bfs和dfs实现。

    费用流

    EK算法:每次找出一条费用最小的流量跑走,这样可以保证最后一定是最大流且此时费用最小。

    zkw费用流:不会,留坑代填

    最小割:给定一张图,问使源点和汇点不连通的最小代价,可以证明这是最大流。

    最小割树:一种神奇的树,构建方法如下,1、从当前图中随便找出两个点,跑一边最小割求出此时两个点之间的最小割,在这两个点间连边,然后我们把满流的边删掉,这样会把图分成好几个块,我们令s所在块为一块,剩下的点为一块,在分治下去,这样我们一共要跑n-1次最小割,连出来的一定是一颗树。

    有一个结论,任意两点之间的最小割是最小割树上两点路径上边权最小值。为什么,考虑我们把图分为两块的时候,当前的最小割就是我们把图分为两块的最小割,也就是从两块中任意拿出两个点,它们之间的最小割的上界。

    二元关系最小割

    有这样的一个问题,有多组二元关系,形如xy都选的价值,选一个的价值,都不选的价值,可以建出这么一张图。

    然后我们可以根据所得的信息求出每条边的流量,跑最小割就可以了。

    最大权闭合子图

    有这样一个问题,给定一个DAG,求一个最大权子图,满足这个子图里的所有元素连出去的边指向的点也都在这个子图里。

    我们可以贪心的先把所有正权点都选上 ,但这样可能不满足它是闭合的, 

    所以我们从源点向每个正权点连流量为点权的边,所有负权点向汇点连流量为点权的绝对值的边。

    相当于是说,对于一些不合法的正负点权来说,要么就花正点权的代价不要正点,要么就把负点权选上。

    这张图的最大流,也就是最小割,就是我们的最小代价。

    bzoj4873寿司餐厅:mx^2+cx表示选择一种代号就会产生固定的mx^2代价,每选择一种寿司就会增加x的代价,考虑如下限制,区间lr被选择时,区间[l+1,r]区间[l,r-1]也必须被选择,这样的话闭合子图的模型就出来了,对于每种代号新开一个点,所有长度为1的区间向所对应的代号连边,表示选了这个区间,这种代号产生的代价就必须产生,我们发现答案就是这张图上的最大权闭合子图。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define N 12009
    #define xi 102 
    #define M 1000002
    #define inf 2e9
    using namespace std;
    typedef long long ll;
    queue<int>q;
    int head[N],cur[N],deep[N],tot=1,top,n,m,a[N],b[N],val[N],id[xi][xi],c[N],num;
    ll ans=0;
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    struct edge{
        int n,to,l;
    }e[M];
    inline void add(int u,int v,int l){
        e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;
        e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;
    }
    inline bool bfs(int s,int t){
        memset(deep,0,sizeof(deep));
        memcpy(cur,head,sizeof(head));
        q.push(s);deep[s]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=e[i].n){
                int v=e[i].to;
                if(!deep[v]&&e[i].l){
                    deep[v]=deep[u]+1;
                    q.push(v);
                }
            }
        }
        return deep[t];
    }
    int dfs(int u,int t,int l){
        if(!l||u==t)return l;
        int flow=0,f;
        for(int &i=cur[u];i;i=e[i].n){               //!!!
            int v=e[i].to;
            if(deep[v]==deep[u]+1&&(f=dfs(v,t,min(l,e[i].l)))){
                e[i].l-=f;e[i^1].l+=f;l-=f;flow+=f;
                if(!l)break;
            }
        }
        return flow;
    }
    int main(){
        n=rd();m=rd(); 
        for(int i=1;i<=n;++i)a[i]=rd(),b[i]=a[i];
        sort(b+1,b+n+1);
        top=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;++i)c[i]=lower_bound(b+1,b+top+1,a[i])-b;
        for(int i=1;i<=n;++i)
          for(int j=i;j<=n;++j){
            id[i][j]=++num;val[num]=rd();
            if(id[i-1][j])add(id[i-1][j],id[i][j],inf);
            if(id[i][j-1])add(id[i][j],id[i][j-1],inf);
          }    
        int T=num+top;
        for(int i=1;i<=n;++i)val[id[i][i]]-=a[i],add(id[i][i],num+c[i],inf),val[num+c[i]]=-m*a[i]*a[i];
        for(int i=1;i<=T;++i){
            if(val[i]>0)add(0,i,val[i]),ans+=val[i];
            else add(i,T+1,-val[i]);
        }
        while(bfs(0,T+1))ans-=dfs(0,T+1,inf);
        printf("%lld
    ",ans); 
        return 0;
    } 
    View Code

    有上下界的网络流

    有上下界的网络流可以用来解决每条边的流量是一个区间的问题。

    无源汇有上下界的可行流(环流):我们可以先让每条边都跑上流量的下界,但这样流量有可能不守恒,所以我们新建源点和汇点,令每个点当前流入量和流出量差的绝对值为x。

    如果流入量大于流出量,我们就从源点向该点连流量为x的边,相当于让这个点流出x的流量,反之我们从该点向汇点连流量为x的边,相当于让这个点再流进x的流量。

    那么我们新加入的每条边都导入/导出了我们一开始直接跑掉流量下界时造成的流入量和流出量的差。

    跑完最大流之后检查一下后加入的边是否满流,如果满流,说明每条边都满足了要求。

    有源汇有上下界的可行流:这个和上面的差别就是有了源汇,其实我们可以通过转换变成上面的问题,判断流了多少流量,只需要判断t-s边的流量就好了。

    有源汇有上下界的最大流:先跑可行流,此时下界已经满足合法,我们可以把超级源和超级汇去掉,在残量网络上跑最大流就可以了,最终的最大流流量=可行流流量+新增广出的流量,其实这个可行流量在我们第二次最大流的时候就自动加上了,其实就是反边的流量。

    有源汇有上下界的最小流:这个比较难理解,我们先不加t-s的边,然后跑一个可行流,再把t-s边加上,再跑一个可行流,那么t-s的流量就是最小流,因为我们要满足流量下限的限制,所以这一步可以尽量填充较多的流量,减小第二次产生的流量。

    bzoj4464[Jsoi2013]旅行时的困惑:裸的树上最小路径覆盖模型,可以用费用流做,甚至可以用树形dp做,用最小流的话图上边设为(1,inf),s到i设为(0,inf),i到t设为(0,inf),跑有源汇最小流。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define N 101002
    #define inf 2e9
    using namespace std;
    queue<int>q;
    int head[N],tot=1,deep[N],cur[N],n,du[N],ans;
    inline int rd(){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    struct edge{int n,to,l,f;}e[N<<4];
    inline void add(int u,int v,int l){
        e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;
        e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;
    }
    inline bool bfs(int s,int t){
        memset(deep,0,sizeof(deep));
        memcpy(cur,head,sizeof(cur));
        q.push(s);deep[s]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=e[i].n){
                int v=e[i].to;
                if(!deep[v]&&e[i].l){deep[v]=deep[u]+1;q.push(v);}
            }
        }
        return deep[t];
    }
    int dfs(int u,int t,int l){
        if(!l||u==t)return l;
        int flow=0,f;
        for(int &i=cur[u];i;i=e[i].n){
            int v=e[i].to;
            if(deep[v]==deep[u]+1&&(f=dfs(v,t,min(l,e[i].l)))){
                e[i].l-=f;e[i^1].l+=f;flow+=f;l-=f;
                if(!l)break;
            }
        }
        return flow;
    }
    inline int dinic(int s,int t){
        int ans=0;
        while(bfs(s,t))while(int f=dfs(s,t,inf))ans+=f;
        return ans;
    }
    int main(){
        n=rd();int x,y;
        for(int i=1;i<n;++i){
            x=rd();y=rd();x++;y++;
            add(y,x,inf);
            du[x]++;du[y]--;
        }
        for(int i=1;i<=n;++i)add(0,i,inf),add(i,n+1,inf);
        for(int i=1;i<=n;++i)
            if(du[i]<0)add(i,n+3,-du[i]);else add(n+2,i,du[i]);
        dinic(n+2,n+3);
        add(n+1,0,inf);
        cout<<dinic(n+2,n+3); 
        return 0;
    }
    View Code

    未完待续。。。

  • 相关阅读:
    LOJ#6501. 「雅礼集训 2018 Day4」Cube 题解
    LOJ#6510. 「雅礼集训 2018 Day8」A 题解
    LOJ#6513. 「雅礼集训 2018 Day10」足球大战 题解
    LOJ#6507. 「雅礼集训 2018 Day7」A 题解
    LOJ#6038. 「雅礼集训 2017 Day5」远行 题解
    Luogu P4208 [JSOI2008]最小生成树计数
    CodeForces 916D Jamie and To-do List
    CodeForces 573B Bear and Blocks
    CodeForces 460C Present
    CodeForces 786B Legacy
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10155707.html
Copyright © 2011-2022 走看看