• 并不对劲的餐巾计划问题


    三倍经验题!

    ********************题目略长**********************

    题目描述

    一个餐厅在相继的N天里,第i天需要ri块餐巾(i=l,2,…,N)。餐厅可以从三种途径获得餐巾。

    (1)购买新的餐巾,每块需p分;

    (2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p)。如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此。

    (3)把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f)。

    在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部。在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当天的需求量Ri,并使N天总的费用最小

    输入输出格式

    输入格式:

    输入文件共3行,第1行为总天数;第2行为每天所需的餐巾块数;第3行为每块餐巾的新购费用p,快洗所需天数m,快洗所需费用f,慢洗所需天数n,慢洗所需费用s。

    输出格式:

    输出文件共1行为最小的费用。

    输入输出样例

    输入样例:
    3
    3 2 4
    10 1 6 2 3
    
    输出样例:
    64
    

    说明

    N<=2000

    ri<=10000000

    p,f,s<=10000

    时限4s

    *************在网络流24题中算是神奇的一题(其实并不一定是网络流)************

    网络流24题中,存在各种只是来让人练写模板熟练度的题。不过这题的建图方式算是一股清流,让人很不好想,但想到了也很不好证明,不过证明出来后真的很好写。

    对于送到快洗部、慢洗部、直接购买都很好想,拆点后直接连就行了。也就是把一天拆成上午和下午两个点,上午可以接受洗完的餐巾、买新餐巾,下午可以把餐巾送去洗。也就是说,上午的“流”的意义是干净的餐巾,下午的“流”的意义是脏的餐巾。

    让第i天“经过”汇点的流为ri显然是行不通的。可以把“拿去用ri个餐巾”拆成两个动作:上午把ri个干净的餐巾给汇点,下午接收从源点来的ri个脏餐巾。

    还要注意的是,餐厅里可以攒干净的餐巾,所以第i天上午要连一条边到第i+1天上午。

    以上过程其实可以用有上下界网络流来思考,但并不对劲的人并不想这么做。

    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define ll long long
    #define maxn 4010
    #define maxm 2000010
    using namespace std;
    const ll inf=0x7fffffff;
    ll fir[maxn],nxt[maxm],v[maxm],fl[maxm],w[maxm],cnt;
    ll mincost,n,m,p,f,s,N,dis[maxn];
    bool vis[maxn];
    ll read()
    {
        ll x=0,f=1;
        char ch=getchar();
        while(isdigit(ch)==0 && ch!='-')
            ch=getchar();
        if(ch=='-')f=-1,ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',
            ch=getchar();
        return x*f;
    }
    void addedge(ll u1,ll v1,ll fl1,ll w1)
    {
        w[cnt]=w1,v[cnt]=v1,fl[cnt]=fl1,nxt[cnt]=fir[u1],fir[u1]=cnt++;
        w[cnt]=-w1,v[cnt]=u1,fl[cnt]=0,nxt[cnt]=fir[v1],fir[v1]=cnt++;
    }
    ll spfa() 
    {
        memset(dis,-1,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dis[N*2+1]=0;
        deque<ll >q;
        q.push_back(N*2+1);
        while(!q.empty())
        {
            ll u=q.front();q.pop_front();
            for(ll k=fir[u];k!=-1;k=nxt[k])
            {
                ll vv=v[k];
                if(fl[k^1]>0)
                {
                    if(dis[vv]>dis[u]-w[k] 
                    || dis[vv]==-1)
                    {
                        dis[vv]=dis[u]-w[k];
                        if(vis[vv]==0)    
                        {
                            if(q.empty()==0&&
                            dis[q.front()]>dis[vv])
                                q.push_front(vv);
                            else
                                q.push_back(vv);
                        }    
                        vis[vv]=1;
                    }
                }
            }
            vis[u]=0;
        }
        return dis[0];
    }
    ll getfl(ll u,ll nowflow)
    {
        vis[u]=1;
        if(u==N*2+1)return nowflow;
        ll tmp,sum=0;
        for(ll k=fir[u];k!=-1;k=nxt[k])
        {
            if(nowflow<=0)break;
            ll vv=v[k];
            if(vis[vv]==0 && fl[k]>0 
            && dis[vv]+w[k]==dis[u]&&
            (tmp=getfl(vv,min(nowflow,fl[k])))>0)
            {
                fl[k]-=tmp;
                fl[k^1]+=tmp;
                nowflow-=tmp;
                mincost+=tmp*w[k];
                sum+=tmp;
            }
         } 
        return sum;
    }
    int main()
    {
        memset(fir,-1,sizeof(fir));
        N=read();
        for(ll i=1;i<=N;i++)
        {
            ll r=read();
            addedge(i,N*2+1,r,0);
            addedge(0,i+N,r,0);
        }
        p=read(),m=read(),f=read(),n=read(),s=read();
        for(ll i=1;i<=N;i++)
        {
            addedge(0,i,inf,p);
            if(i+m<=N)addedge(i+N,i+m,inf,f);
            if(i+n<=N)addedge(i+N,i+n,inf,s);
            if(i+1<=N)addedge(i,i+1,inf,0);
        }
        while(spfa()!=-1)
        {
            memset(vis,0,sizeof(vis));
            getfl(0,0x7fffffff);
        }
        cout<<mincost;
        return 0;
    }
    并不对劲的费用流

    ex1(BJWC):如果n<=2 * 10^5,ri<=100该怎么做?

    其实可以三分购买的毛巾总数,然后贪心地算出是否可行。

    假设购买的毛巾总数是给定的,那么这道题就变得简单多了。在第x天将毛巾送去洗,到第x+m或x+n天才能用,这件事可以看成在第x天毛巾不够用时,花s或f乘坐时光机回到第x-n或x-m天,将毛巾变成干净的带回来。对于前面的若干天,新买的毛巾不用白不用,尽量把新买的用完。新毛巾不够用时考虑回到之前的某一天。这时肯定回到x-m或再之前的时间更划算。因为x-m及以前可以用慢洗,花费少。如果x-m天及之前没有脏毛巾了,才去第x-n到x-m天之间。这时最好取离现在更近的。这样可以让离现在更远的时间,也就是离第x-m天更近的时间,更有机会慢洗。这个过程可以用双端队列维护。

    根据这个策略,对于每一个购买的毛巾总数c,都有唯一确定的总费用与之对应。因此定义f(c)为总共购买c块毛巾的费用。注意c的取值范围应该是大于某个数,因为新毛巾总数太少的时候有些天会没毛巾可用。

    三分法要保证单峰。感性地想,c较小时要频繁地使用快洗,所以花在洗毛巾上的费用会很大。而c较大时购买毛巾的费用会很大。

    理性地想,并不对劲的人并不理性。

    设ri之和为R,这样就可以做到时间复杂度O(n log R)了呢。

    ex2:在ex1的条件下,若快洗店更便宜,该怎么办?

    那就别慢洗了。

    是贪心能解所有网络流问题,还是网络流能解所有贪心问题呢(就是时间复杂度……)?

  • 相关阅读:
    后缀运算符与前缀运算符的区别
    冒泡排序
    pc端遇到的知识点
    Windows 上传文件到Android 模拟器报错 Failed to push selection: Invalid argument
    Android错误 transfer error: Readonly file system
    Android双击返回键退出Activity
    Android应用添加(创建)和删除及判断是否存在桌面快捷方式
    Aandroid 总结4种线程中操作UI界面的方法
    Could not load file or assembly 'System.Data.SQLite' or one of its dependencies. An attempt was made to load a program
    学习
  • 原文地址:https://www.cnblogs.com/xzyf/p/8317280.html
Copyright © 2020-2023  润新知