• [学习笔记]费用流


    一些内容在另一篇博客

    [学习笔记]网络

     

     

    有的时候要保证最大的情况下,费用尽可能优。

    就要用费用流了。

    目前所涉及的费用流,都是在最大流的前提下

     

    所以,当题目可以转化成,在保证。。。的情况下,最优化。。。

    也许就可以尝试费用流了。

     

    (同样意味着选择,

    最小割没有什么最大流的前提,可以没有什么限制地,割掉一些边即可。

    但是,每条边必须割掉,不能“割一部分”,对于一些可以剩下的模型就捉襟见肘了。

     

    用EK。因为dinic不能保证流出来的是最优代价的。

    至于为什么每次贪心选择最优的路径,最后就是最优的,可能的原因是,因为有反边,所以自带反悔自动机功能。贪心没有问题。

    可以延伸出来一个结论:

    最短路费用流,当前费用是所有能够到达当前总流量的所有费用中最优的一个。

    (伪证:

    因为贪心成立。还没有听说过哪个贪心不是局部最优解,却是全局最优解的2333

     

    例题:

    方格取数、晨跑。

    拆点费用流即可。

     

    修车,美食节

    [NOI2012]美食节——费用流(带权二分图匹配)+动态加边

    拆点费用流。

    边权设计:

    反过来考虑每个人站在某个位置,对后面的人等待时间产生的影响。

    第j个阶段,意味着后面还有j个。这样,贡献就只和自己有关系了。

    必要的时候动态加边。

    [SDOI2016]数字配对

    倍数关系,这个倍数关系还是质数。

    那么,这两个数的质因子一定相差一,差的那个质因子就是这个质数。

    所以,把数按照质因子个数奇偶性分成左右两类

    左部点右部点自己之间不会有配对。

    左部右部点之间,如果成倍数关系,并且一个是另一个质因子个数+1,那么连边流inf,费c1*c2。

    左部点和S,连流b费0

    右部点和T,连流b费0

    这样,每个流就是一个配对。

     

    至于

    “在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。”

    费用流的特点是,

    “可以保证当前的费用永远是当前流量下最优的”

    所以,如果当前总费用<0

    那么就可以停止了。因为之后,流量更大的话,费用也不可能更高。

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=200*2+5;
    const int M=200*200+200+200;
    const int U=31622+20;
    const int inf=0x3f3f3f3f;
    int n,m,s,t;
    struct node{
        int nxt,to;
        int w;
        ll v;
    }e[2*M];
    int hd[N],cnt=1;
    void add(int x,int y,int z,ll c){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        e[cnt].w=z;
        e[cnt].v=c;
        hd[x]=cnt;
    }
    struct po{
        int a,b,c;
        int cnt;
        bool friend operator <(po a,po b){
            return a.a<b.a;
        }
    }a[N];
    int pri[M],tot;
    bool vis[U];
    void sieve(){
        vis[1]=1;
        for(reg i=2;i<=U-10;++i){
            if(!vis[i]){
                pri[++tot]=i;
            }
            for(reg j=1;j<=tot;++j){
                if((ll)pri[j]*i>U-10) break;
                vis[pri[j]*i]=1;
                if(i%pri[j]==0) break;
            }
        }
    }
    int che(int x){
        int ret=0;
        for(reg i=1;i<=tot&&pri[i]*pri[i]<=x;++i){
            if(x%pri[i]==0){
                while(x%pri[i]==0) x/=pri[i],++ret;
            }    
        }
        if(x!=1) ++ret;
        return ret;
    }
    ll d[N];
    int pre[N];
    int incf[N];
    queue<int>q;
    ll now,flow;
    bool in[N];
    bool spfa(){
        memset(d,0xcf,sizeof d);
        while(!q.empty()) q.pop();
        d[s]=0;
        q.push(s);
        pre[s]=0;incf[s]=inf;
        while(!q.empty()){
            int x=q.front();q.pop();
            in[x]=0;
    //        cout<<"x "<<x<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
        //        cout<<" yy "<<y<<endl;
                if(e[i].w){
                    if(d[y]<d[x]+e[i].v){
                        d[y]=d[x]+e[i].v;
                        pre[y]=i;
                        incf[y]=min(incf[x],e[i].w);
                        if(!in[y]) {
                            in[y]=1;
                            q.push(y);
                        }
                    } 
                }
            }
        }
    //    cout<<" spfa "<<d[t]<<endl;
        if(d[t]==-3472328296227680305ll) return false;
        return true;
    }
    bool upda(){
        //cout<<" d[t] "<<d[t]<<endl;
        if(now+d[t]*incf[t]<0){
            flow+=now/abs(d[t]);
            return false;
        }
        int x=t;
        while(pre[x]){
            //cout<<"upda "<<x<<endl;
            e[pre[x]].w-=incf[t];
            e[pre[x]^1].w+=incf[t];
            x=e[pre[x]^1].to;
        }
        flow+=incf[t];
        now+=d[t]*incf[t];
        //cout<<" now "<<now<<" "<<flow<<endl;    
        return true;
    }
    int le[N],ri[N];
    int lc,rc;
    int main(){
        scanf("%d",&n);
        for(reg i=1;i<=n;++i){
            rd(a[i].a);
        }for(reg i=1;i<=n;++i) rd(a[i].b);
        for(reg i=1;i<=n;++i) rd(a[i].c);
        sieve();
        for(reg i=1;i<=n;++i){
            a[i].cnt=che(a[i].a);
            if(a[i].cnt%2==0){
                le[++lc]=i;
            }else ri[++rc]=i;
        }
        for(reg i=1;i<=lc;++i){
            int id=le[i];
            for(reg j=1;j<=rc;++j){
                int to=ri[j];
                if(abs(a[id].cnt-a[to].cnt)==1){
                    if(a[id].a%a[to].a==0||a[to].a%a[id].a==0){
                        add(id,to,inf,(ll)a[id].c*a[to].c);
                        add(to,id,0,-(ll)a[id].c*a[to].c);
                    }
                }
            }
        }
        s=n+1,t=n+2;
        for(reg i=1;i<=lc;++i){
            int id=le[i];
            add(s,id,a[id].b,0);
            add(id,s,0,0);
        }
        for(reg i=1;i<=rc;++i){
            int to=ri[i];
            add(to,t,a[to].b,0);
            add(t,to,0,0);
        }
    //    cout<<" cnt "<<cnt<<endl;
        while(spfa()){
        //    cout<<" xx "<<endl;
            bool fl=upda();
            if(!fl) break;
        }
        printf("%lld",flow);
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/27 15:59:23
    */
    数字配对

    [SDOI2013]费用流

    看似费用流。

    其实发现,Bob一定选择最大的流量*p

    所以,Alice要使得所选择的最大流的最大流量最小。

    二分判断即可。

    注意,边最大流量不一定是整数。因为可以均摊成小数变得更低。

    eps 1e-5即可。

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=103;
    const int M=1000+3;
    const int inf=1023333333.00;
    const double eps=1e-8;
    int n,m,p,s,t;
    struct bian{
        int x,y,c;
    }b[M];
    struct node{
        int nxt,to;
        double w;
    }e[2*M];
    int hd[N],cnt=1;
    void add(int x,int y,double z){
        //cout<<" x y z "<<x<<" "<<y<<" "<<z<<endl;
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        e[cnt].w=z;
        hd[x]=cnt;
    }
    int d[N];
    double dfs(int x,double flow){
        if(x==t) return flow;
        double res=flow;
        for(reg i=hd[x];i&&fabs(res)>=eps;i=e[i].nxt){
            int y=e[i].to;
            if(e[i].w&&d[y]==d[x]+1){
                double k=dfs(y,min(res,e[i].w));
                if(fabs(k)<eps) d[y]=0;
                e[i].w-=k;
                e[i^1].w+=k;
                res-=k;    
            }
        }
        return flow-res;
    }
    int q[2*N],l,r;
    double ans;
    bool bfs(){
        memset(d,0,sizeof d);
        d[s]=1;
        l=1,r=0;
        q[++r]=s;
        while(l<=r){
            int x=q[l++];
            //cout<<" xxx "<<x<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                //cout<<" yy "<<y<<" "<<e[i].w<<endl;
                if(fabs(e[i].w)>=eps&&!d[y]){
                    d[y]=d[x]+1;
                    q[++r]=y;
                    if(y==t) return true;
                }
            }
        }
        return false;
    }
    double dinic(){
        double ret=0;
        double flow=0;
        while(bfs()){
            while((flow=dfs(s,inf))>=eps) ret+=flow;
        }
        return ret;
    }
    int main(){
        scanf("%d%d%d",&n,&m,&p);
        int x,y,c;
        s=1,t=n;
        double R=0.00;
        for(reg i=1;i<=m;++i){
            rd(x);rd(y);rd(c);
            R=max(R,(double)c);
            b[i].x=x,b[i].y=y;b[i].c=c;
            add(x,y,(double)c);
            add(y,x,0);
        }
        int ans=dinic();
        printf("%d
    ",ans);
        double L=0.00;
        R+=2.33;
        double op=0.00;
        while(R-L>=eps){
            double mid=(L+R)/2;
            memset(hd,0,sizeof hd);cnt=1;
            for(reg i=1;i<=m;++i){
                add(b[i].x,b[i].y,min((double)b[i].c,mid));
                add(b[i].y,b[i].x,0);
            }
            double now=dinic();
            if(now<ans){
                L=mid;
            }else{
                R=mid;op=mid;
            }
        }
        printf("%lf",op*p);
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/27 17:09:49
    */
    费用流

    upda:2019.2.11

    由于贪心成立,

    所以,费用流的一个优秀的性质是,当前选择的,就是当前的最优解

    在一些基于费用流的剪枝中,经常会用到费用单调的性质

    即每一次的费用都大于等于上一次

    这个可以证明


    upda:2019.6.11

    无源汇上下界最小费用可行流,消负环?

    用循环流填充负环。

    法一:spfa时候找到负环,不断找pre,整个负环的流量就是容量最小的边。暴力填满

    法二: 

    先砍掉下界。但是先不处理下界带来的盈余流。

    先处理负循环流

    类似上下界网络流,钦定负边直接流满,注意这里可以反悔!也就是反向边有流量!

    超级源汇,最小费用最大流,处理每个点关于负边产生的盈余流。然后就把负循环流填满了。

    然后,再和上下界可行流一样,

    干掉下界带来的盈余流,超级源超级汇,最小费用最大流处理掉。

    至于有源汇的循环流有什么实际意义,,,,我也不清楚

    反正在无源汇上下界中,只有循环流了。

  • 相关阅读:
    J2EE13个规范--【J2EE】
    事件监听、持有对方的引用--【J2SE】
    TCP协议:服务端和客户端demo--【J2SE】
    线程:Interrupt、Sleep、Join、线程同步--【J2SE】
    1. Visual C++ 6.0 安装和使用
    无法连接虚拟设备sata0:1,因为主机上没有相应的设备
    Linux文件夹文件创建、删除
    服务器
    linux 下 apache启动、停止、重启命令
    Apache部署静态html
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10028021.html
Copyright © 2020-2023  润新知