• CF808E Selling Souvenirs


    题目链接:

    http://codeforces.com/contest/808/problem/E

    题目大意:

      Petya 有 n 个纪念品,他能带的最大的重量为 m,各个纪念品的重量为 wi,花费为 ci,问 Petya 能带的纪念品的最大价值几何?

    心得:

      刚开始以为是01背包,开开心心地写了个dp上去超时ORZ。后来想要用记忆化,发现开不出这么大的数组,所以想了很久也想不出个所以然。

      后来经一位大神一篇博文的点拨(链接:http://www.cnblogs.com/wmrv587/p/6876314.html),决定用三分法试试。

      于是看了一篇介绍三分查找的博文(链接:http://blog.csdn.net/pi9nc/article/details/9666627)

      然后动手写了第一版代码:

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <functional>
     4 using namespace std;
     5 const int MAXN=100000+5;
     6 int cost[5][MAXN];
     7 int t[5];
     8 int n,m;
     9 long long sum[5][MAXN];
    10 int main()
    11 {
    12     scanf("%d%d",&n,&m);
    13     for(int i=1;i<=n;i++){
    14         int k;
    15         scanf("%d",&k);
    16         scanf("%d",&cost[k][++t[k]]);
    17     }
    18     for(int i=1;i<=3;i++)   sort(cost[i]+1,cost[i]+t[i]+1,greater<int>());
    19 
    20     for(int i=1;i<=3;i++)
    21         for(int j=1;j<=t[i];j++)
    22             sum[i][j]=sum[i][j-1]+cost[i][j];
    23     long long ans=0;
    24     for(int i=0;i<=m;i++){
    25         int l=0,r=min(t[2],i/2);                      //以2重量的来计算
    26         while(l<r){
    27             int mid=(l+r)/2,mmid=(mid+r)/2;
    28             int t11=min(i-mid*2,t[1]),t12=min(i-mmid*2,t[1]);
    29             if(sum[2][mid]+sum[1][t11]>sum[2][mmid]+sum[1][t12])
    30                 r=mmid;
    31             else
    32                 l=mid+1;
    33         }
    34         int t1=min(i-l*2,t[1]);
    35         long long temp=sum[2][l]+sum[1][t1];
    36         int t3=min(t[3],(m-i)/3);
    37         temp+=sum[3][t3];
    38         ans=max(ans,temp);
    39     }
    40     printf("%I64d
    ",ans);
    41     return 0;
    42 }
    View Code

      结果Wrong answer on test 8

      在那里debug了2个小时,把它改成了这样:

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <functional>
     4 using namespace std;
     5 const int MAXN=100000+5;
     6 int cost[5][MAXN];
     7 int t[5];
     8 int n,m;
     9 long long sum[5][MAXN];
    10 int main()
    11 {
    12     scanf("%d%d",&n,&m);
    13     for(int i=1;i<=n;i++){
    14         int k;
    15         scanf("%d",&k);
    16         scanf("%d",&cost[k][++t[k]]);
    17     }
    18     for(int i=1;i<=3;i++)   sort(cost[i]+1,cost[i]+t[i]+1,greater<int>());
    19 
    20     for(int i=1;i<=3;i++)
    21         for(int j=1;j<=t[i];j++)
    22             sum[i][j]=sum[i][j-1]+cost[i][j];
    23 
    24     long long ans=0;
    25     for(int i=0;i<=m;i++){
    26          int l=0,r=min(t[2],i/2);                      //以2重量的来计算
    27        for(int k=0;k<100;k++){
    28             int mid=(l+r)/2,mmid=(mid+r)/2;
    29             if(sum[2][mid]+sum[1][i-mid*2]>=sum[2][mmid]+sum[1][i-mmid*2])
    30                 r=mmid;
    31             else
    32                 l=mid;
    33         }
    34         int t1=min(i-l*2,t[1]);
    35         long long temp=sum[2][l]+sum[1][t1];
    36         int t3=min(t[3],(m-l*2-t1)/3);
    37         temp+=sum[3][t3];
    38         ans=max(ans,temp);
    39     }
    40     printf("%I64d
    ",ans);
    41     return 0;
    42 }
    View Code

      但还是WA。

      于是去参考排行榜上很靠前的一位选手的做法,发现其中一位的做法跟我很相似,但是他在 l-r<=30 的时候就停止了三分查找,然后再遍历 [l,r] 这个区间,找出最优解。

      仿照这个做法,我写了第三个版本:

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <functional>
     4 using namespace std;
     5 const int MAXN=100000+5;
     6 int cost[5][MAXN];
     7 int t[5];
     8 int n,m;
     9 long long sum[5][MAXN];
    10 int main()
    11 {
    12     scanf("%d%d",&n,&m);
    13     for(int i=1;i<=n;i++){
    14         int k;
    15         scanf("%d",&k);
    16         scanf("%d",&cost[k][++t[k]]);
    17     }
    18     for(int i=1;i<=3;i++)   sort(cost[i]+1,cost[i]+t[i]+1,greater<int>());
    19 
    20     for(int i=1;i<=3;i++)
    21         for(int j=1;j<=t[i];j++)
    22             sum[i][j]=sum[i][j-1]+cost[i][j];
    23 
    24     long long ans=0;
    25     for(int i=0;i<=m;i++){
    26         int l=0,r=min(t[2],i/2);
    27         while(r-l>30){
    28             int mid=(l+r)/2,mmid=(mid+r)/2;
    29             if(sum[2][mid]+sum[1][i-mid*2]>=sum[2][mmid]+sum[1][i-mmid*2])
    30                 r=mmid;
    31             else
    32                 l=mid;
    33         }
    34         int t1=min(t[1],i-2*l);
    35         long long maxc=sum[2][l]+sum[1][t1];
    36         int maxn=l,maxm=t1+l*2;
    37         for(int j=l+1;j<=r;j++){
    38             int tt1=min(t[1],i-2*j);
    39             if(sum[2][j]+sum[1][tt1]>maxc){
    40                 maxn=j;
    41                 maxc=sum[2][j]+sum[1][tt1];
    42                 maxm=j*2+tt1;
    43             }
    44         }
    45         long long temp=maxc;
    46         int t3=min(t[3],(m-maxm)/3);
    47         temp+=sum[3][t3];
    48         ans=max(ans,temp);
    49     }
    50     printf("%I64d
    ",ans);
    51     return 0;
    52 }
    View Code

      终于AC了!

      后来研究发现,用最后一种作法,在三分得出的区间内得出的峰值跟直接三分得到的峰值并不一致。

      

      拓展思考:以后当发现直接三分(二分)查找得出的结果有问题时,可尝试先找出一个区间即可,在这个区间里遍历找出最优解。

      

      

      

    “这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
  • 相关阅读:
    面试题:Spring的理解
    静态块与静态变量
    构造方法与构造块的执行顺序(区别于static)
    子类调用父类的构造函数几种情况
    使用json遇到的问题
    页面加载,使用ajax查询某个类别,并且给它们添加(拼接)连接
    对某个商品的上下架状态进行改变
    SSH后台管理系统,实现查询+分页
    跟上Java8
    java中值传递和引用传递
  • 原文地址:https://www.cnblogs.com/Blogggggg/p/6898716.html
Copyright © 2020-2023  润新知