zoukankan      html  css  js  c++  java
  • [网络流24题] 餐巾计划问题 [费用流]

    题面:

    https://www.luogu.org/problemnew/show/P1251

    思路:

    这道题乍一看,可以跑上下界费用流

    代码量、难度 -> inf

    其实不然,我们可以用费用流的特殊处理去掉下界

    观察题目,每天要求有ri块餐巾

    首先,有贪心如下:

    当且仅当每天可供使用的餐巾正好满足需求时,可以有最小费用

    证明:若某一天有多一块餐巾,则其根本来源一定是买多了,而且在这块餐巾参与的周转中还消费了一些清洗费用,同时它造成其余的日子里也会有餐巾被闲置

    因此首先把题目转化为“每天正好使用ri”

    此时考虑拆点,将每一天拆成xi,yi,其中yi连边到T,容量为ri,费用为0,表示该天需要这么多餐巾。(以下这类边表示为 (cap,cost) )

    然后开始建图,思路是保证yi到T的边可以满流的情况下,构建费用等同于题目要求的网络。

    首先定义xi为“第i天对于用完的餐巾做出的决策”,因此可以从xi到xi+1连一条边 (inf,0),其边上的流量表示“第i+1天保留第i天中(流量)块餐巾的决策”

    然后从S到xi连边 (ri,0) ,代表该天需要做出这一决策的餐巾数量,也就是该天使用了的餐巾数量

    从S到yi连边(inf,p)代表新买一块。这块餐巾不需要参与xi的“旧餐巾决策”,直接连到yi

    从xi到yi+m、yi+n分别连边 (inf,f) (inf,s) ,代表快洗和慢洗决策。

    然后在这张图上跑S-T最小费用最大流即可

    Code:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define inf 100000000000000LL
    #define ll long long
    using namespace std;
    inline ll read(){
        ll re=0,flag=1;char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
        return re;
    }
    ll n,m,cnt,ans,flow,first[100010];
    ll dis[100010],pre[100010],path[100010];
    struct edge{
        ll to,next,w,cap;
    }a[100010];
    inline void add(ll u,ll v,ll w,ll cap){
    //    cout<<"add "<<u<<ends<<v<<ends<<w<<ends<<cap<<endl;
        a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
        a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
    }
    void init(){
        memset(first,-1,sizeof(first));
        memset(a,0,sizeof(a));cnt=-1;ans=0;flow=0;
    }
    ll spfa(ll s,ll t){
        ll q[10010],head=0,tail=1,maxn=10000,i,u,v,w;
        memset(pre,-1,sizeof(pre));
        for(i=s;i<=t;i++) dis[i]=inf;
        q[0]=s;dis[s]=0;
        while(head!=tail){
            u=q[head];head=(head+1)%maxn;
    //        cout<<"spfa "<<u<<endl;
            for(i=first[u];~i;i=a[i].next){
                v=a[i].to;w=a[i].w;
    //            cout<<"    to "<<v<<ends<<dis[v]<<ends<<w+dis[u]<<endl;
                if(a[i].cap&&dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    pre[v]=u;path[v]=i;
                    q[tail]=v;tail=(tail+1)%maxn;
                }
            }
        }
        return ~pre[t];
    }
    inline ll _min(ll l,ll r){return (l<r)?l:r;}
    ll mcmf(ll s,ll t){
        ll f,u;
        while(spfa(s,t)){
            f=inf;
            for(u=t;u!=s;u=pre[u]) f=_min(f,a[path[u]].cap);
            flow+=f;ans+=dis[t]*f;
    //        cout<<"mcmf "<<flow<<ends<<ans<<endl;
            for(u=t;u!=s;u=pre[u]){
                a[path[u]].cap-=f;
                a[path[u]^1].cap+=f;
            }
        }
    }
    int main(){
    //    freopen("napkin.in","r",stdin);
    //    freopen("napkin.out","w",stdout);
        init();
        ll i,t1,t2;
        n=read();
        for(i=1;i<=n;i++){
            t1=read();
            add(0,i,0,t1);
            add(i+n,(n<<1)+1,0,t1);
        }
        for(i=1;i<n;i++) add(i,i+1,0,inf);
        t1=read();
        for(i=1;i<=n;i++) add(0,i+n,t1,inf);
        t1=read();t2=read();
        for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf);
        t1=read();t2=read();
        for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf);
        mcmf(0,(n<<1)+1);
        printf("%lld",ans);
    }
  • 相关阅读:
    Ajax实现异步上传图片
    python文章的抓取
    python
    Python的MySQLdb模块安装
    import _mysql----ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。
    安装第三方模块时遇到Python version 2.7 required, which was not found
    beautifulSoup安装
    安装setuptools和pip
    python 的简单抓取图片
    python
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8387692.html
Copyright © 2011-2022 走看看