zoukankan      html  css  js  c++  java
  • Bzoj 1229: [USACO2008 Nov]toy 玩具 题解 三分+贪心

    1229: [USACO2008 Nov]toy 玩具

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 338  Solved: 136
    [Submit][Status][Discuss]

    Description

    玩具 [Chen Hu, 2006] Bessie的生日快到了, 她希望用D (1 <= D <= 100,000; 70%的测试数据都满足 1 <= D <= 500)天来庆祝. 奶牛们的注意力不会太集中, 因此Bessie想通过提供玩具的方式来使它们高兴. 她已经计算出了第i天需要的玩具数T_i (1 <= T_i <= 50). Bessie的幼儿园提供了许多服务给它们的奶牛程序员们, 包括一个每天以Tc (1 <= Tc <= 60)美元卖出商品的玩具店. Bessie想尽可能的节省钱, 但是Farmer John担心没有经过消毒的玩具会带来传染病(玩具店卖出的玩具是经过消毒的). 有两种消毒的方式. 第1种方式需要收费C1美元, 需要N1个晚上的时间; 第2种方式需要收费 C2美元, 需要N2个晚上的时间(1 <= N1 <= D; 1 <= N2 <= D; 1 <= C1 <= 60; 1 <= C2 <= 60). Bessie在party结束之后把她的玩具带去消毒. 如果消毒只需要一天, 那么第二天就可以拿到; 如果还需要一天, 那么第三天才可以拿到. 作为一个受过教育的奶牛, Bessie已经了解到节约的意义. 帮助她找到提供玩具的最便宜的方法.

    Input

    * 第 1 行: 六个用空格隔开的整数 D, N1, N2, C1, C2, Tc

    * 第 2..D+1 行: 第 i+1 行包含一个整数: T_i

    Output

    第 1 行: 提供玩具所需要的最小费用.

    Sample Input

    4 1 2 2 1 3
    8
    2
    1
    6

    输入解释:
    Bessie想开4天的party, 第1天需要8个玩具, 第2天需要2个玩具, 第3天需要1个玩具,
    第4天需要6个玩具. 第一种方式需要$2, 用时1天; 第二种方式需要$1, 用时2天. 买
    一个玩具需要$3.

    Sample Output

    35
    输出解释:
    第 1 天 买8个玩具, 花去$24; 送2个玩具去快洗, 6个慢洗.
    第 2 天 取回2个快洗的玩具, 花去$4. 送1个玩具去慢洗.
    第 3 天 取回6个慢洗的玩具, 花去$6.
    第 4 天 取回所有的玩具(与现有的加在一起正好6个), 花去$1. 这样就用了最少的钱.
     
      好久没刷不是考试的题了……
      liu_runda表示他看不下去我们现在刷的和考的题了,也不知道是不是他搞出来这一波奇奇怪怪的题……
      这道题上来我的打法是这样的:
        我每一天如果说买最便宜就光买就好了。
        对于快洗和慢洗分别开了一个类似于“洗衣池”的东西,记录一下有多少衣服可以被快洗/慢洗。然后对于慢洗(我们假定他比慢洗便宜)我们能用就用,然后看是买便宜还是快洗便宜,如果快洗便宜就从最新的可以快洗的快洗。然后,20分……
      好歹打了一个小时,又花了一个小时证出来它不对,留个代码祭奠一下。
     1 #include <iostream>
     2 #include <cstdlib>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <queue>
     6 #include <algorithm>
     7 #include <cmath>
     8 #include <map>
     9 #include <set>
    10 #define N 100005
    11 using namespace std;
    12 int n,va1,va2,t1,t2,bu,t[N];
    13 int sum[5],ans,st[N],top;
    14 int main()
    15 {
    16     freopen("toy.in","r",stdin);
    17     freopen("toy.out","w",stdout);
    18     scanf("%d%d%d%d%d%d",&n,&t1,&t2,&va1,&va2,&bu);
    19     for(int i=1;i<=n;i++)
    20     {
    21         scanf("%d",&t[i]);
    22     }
    23     if(va1>va2)swap(va1,va2),swap(t1,t2);
    24     //cout<<endl;
    25     int now=0;
    26     for(int i=1;i<=n;i++)
    27     {
    28         if(i>t1) sum[1]+=t[i-t1];
    29         if(i>t2) 
    30         {
    31             sum[2]+=t[i-t2];
    32             top++;
    33             st[top]=i-t2;
    34         }
    35         if(bu<=va1&&bu<=va2)
    36         {
    37             ans+=bu*t[i];
    38             continue;
    39         }
    40         if(sum[1]>=t[i])
    41         {
    42             sum[1]-=t[i];
    43             ans+=va1*t[i];
    44             if(t1>=t2)sum[2]-=t[i];
    45         }
    46         else
    47         {
    48             ans+=va1*sum[1];
    49             if(t1>=t2)
    50             {
    51                 sum[2]-=sum[1];
    52                 if(bu<=va2)
    53                 {
    54                     ans+=bu*(t[i]-sum[1]);
    55                 }
    56                 else
    57                 {
    58                     if(sum[2]>=t[i]-sum[1])
    59                     {
    60                         ans+=va2*(t[i]-sum[1]);
    61                         sum[2]-=(t[i]-sum[1]);
    62                         int js=0;
    63                         while(top)
    64                         {
    65                             if(js+t[st[top]]>=t[i]-sum[1])
    66                             {
    67                                 t[st[top]]-=t[i]-sum[1]-js;
    68                                 if(!t[st[top]])top--;
    69                                 break;
    70                             }
    71                             js+=t[st[top]];
    72                             t[st[top]]=0;
    73                             top--;
    74                         }        
    75                     }
    76                     else
    77                     {
    78                         ans+=va2*sum[2];
    79                         while(top)
    80                         {
    81                             t[st[top]]=0;
    82                             top--;
    83                         }
    84                         ans+=bu*(t[i]-sum[1]-sum[2]);
    85                         sum[2]=0;
    86                     }
    87                 }
    88             }
    89             else
    90             {
    91                 ans+=bu*(t[i]-sum[1]);
    92             }
    93             sum[1]=0;
    94         }
    95         //cout<<ans<<endl;
    96     }
    97     printf("%d
    ",ans);
    98     return 0;
    99 }
    View Code

      为什么这么打不对呢?假设我们快洗和买相差无几,但是慢洗要便宜很多,那么我们在较早的时间快洗会导致之后的慢洗无法使用现在的玩具,导致虽然我们在当时省了一点小钱,但最终我们丢了大钱(好有哲理啊)。

      那么我们到底该怎么办呢?

      我们上面这种打法之所以不对,是因为我们把原本应该买新玩具的地方使用了快洗,那么我们为什么不能通过确定我们总共要买多少玩具来避免呢?当然可以,但对于买的玩具的个数我们枚举肯定是不行的,那么二分可以吗?用十二指肠想一想也知道不行,那么我们能否用三分呢?好像是可以的,假设我们免费买,那么我们买的越多花的钱越少,但实际还是要花钱去买的,所以貌似就是一个三分。之前在cgh_Andy的题解中发现了证明,大家想看可以去看一下(据说还有拿导数证的%%%)。

      那么我们既然知道了三分的值,我们就可以按照类似与我之前的方法进行贪心,唯一不一样的是这次我们能买就买。别的还是照常,维护一个双向队列就好了。

     1 #include <iostream>
     2 #include <cstdlib>
     3 #include <cstdio>
     4 #include <cstring>
     5 #include <queue>
     6 #include <algorithm>
     7 #include <cmath>
     8 #include <map>
     9 #include <set>
    10 #define N 100005
    11 using namespace std;
    12 int n,va1,va2,t1,t2,bu,t[N],a[N];
    13 int sum,mx,q[N],hea,en;
    14 int check(int x)
    15 {
    16     hea=1,en=0;
    17     memset(q,0,sizeof(q));
    18     memcpy(a,t,sizeof(t));
    19     int ans=x*bu;
    20     for(int i=1;i<=n;i++)
    21     {
    22         if(i>t2)
    23         {
    24             en++;
    25             q[en]=i-t2;
    26         }
    27         if(t[i]<=x)
    28         {
    29             x-=t[i];
    30             continue;
    31         }
    32         for(int j=t[i]-x;j;)
    33         {
    34             if(en<hea)return 0x7fffffff;
    35             if(q[hea]<=i-t1)
    36             {
    37                 int k=min(a[q[hea]],j);
    38                 a[q[hea]]-=k,j-=k;
    39                 ans+=va1*k;
    40                 if(!a[q[hea]])hea++;
    41             }
    42             else
    43             {
    44                 int k=min(a[q[en]],j);
    45                 a[q[en]]-=k,j-=k;
    46                 ans+=va2*k;
    47                 if(!a[q[en]])en--;
    48             }
    49         }
    50         if(x)x=0;
    51     }
    52     return ans;
    53 }
    54 int main()
    55 {
    56     scanf("%d%d%d%d%d%d",&n,&t1,&t2,&va1,&va2,&bu);
    57     for(int i=1;i<=n;i++)
    58     {
    59         scanf("%d",&t[i]);
    60         sum+=t[i];
    61         mx=max(mx,t[i]);
    62     }
    63     if(va1>va2)swap(va1,va2),swap(t1,t2);
    64     int li=mx,ri=sum;
    65     while(li<ri-10)
    66     {
    67         int lmid=(li*2+ri)/3,rmid=(li+ri*2)/3;
    68         int ans1=check(lmid),ans2=check(rmid);
    69         if(ans1<ans2)ri=rmid;
    70         else li=lmid;
    71     }
    72     
    73     int ans=0x7fffffff;
    74     for(int i=li;i<=ri;i++)
    75     {
    76         ans=min(check(i),ans);
    77     }
    78     printf("%d
    ",ans);
    79     return 0;
    80 }
    View Code

      Ps:一开始交上去还T了,因为打错了三分,我是不是该找一些三分的题练一下,因为我貌似还没有一次自己打过三分成功的说。

  • 相关阅读:
    windows下安装python模块
    红包demo
    如何查看python 的api
    vscode 与 python 的约会
    默认构造函数
    关于重载
    转类型转换
    asm-offset.h 生成
    debian 7 安装
    emacs 定制进缩风格
  • 原文地址:https://www.cnblogs.com/liutianrui/p/7638727.html
Copyright © 2011-2022 走看看