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了,因为打错了三分,我是不是该找一些三分的题练一下,因为我貌似还没有一次自己打过三分成功的说。

  • 相关阅读:
    poj 1113 Wall 凸包的应用
    NYOJ 78 圈水池 (入门级凸包)
    Monotone Chain Convex Hull(单调链凸包)
    poj Sudoku(数独) DFS
    poj 3009 Curling 2.0(dfs)
    poj 3083 Children of the Candy Corn
    Python join()方法
    通过FISH和下一代测序检测肺腺癌ALK基因融合比较
    华大病原微生物检测
    NGS检测ALK融合大起底--转载
  • 原文地址:https://www.cnblogs.com/liutianrui/p/7638727.html
Copyright © 2011-2022 走看看