zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 21 E

    题目链接:Educational Codeforces Round 21 E - Selling Souvenirs

    题意:

    有n个物品,每个物品有一个重量和价值,现在有一个m大的背包,问你最大能装多少价值。

    题解:

    做法一:

    这题是加强版的01背包,不过有个特别的地方就是w只有三种。

    所以可以枚举其中一种重量,然后三分查找。

    因为剩下两种重量的函数值是一个单峰函数(不会证明)。

    注意:在三分整数的时候,当区间只包含3个点时,有一个点访问不到,要手动补。

     1 #include<bits/stdc++.h>
     2 #define F(i,a,b) for(int i=a;i<=b;i++)
     3 using namespace std;
     4 typedef long long ll;
     5 const int N=1e5+7;
     6 
     7 int n,m,sum;
     8 ll ans,pre[4][N];
     9 vector<int>a[4];
    10 
    11 ll check(int mid,int have)
    12 {
    13     int one=have-2*mid;
    14     if(one>a[1].size())one=a[1].size();
    15     return pre[2][mid]+pre[1][one];
    16 }
    17 
    18 ll del(int have)
    19 {
    20     int l=0,r=a[2].size();
    21     r=min(r,have/2);
    22     while(l<r-1)
    23     {
    24         int mid=l+r>>1,mid2=r+mid>>1;
    25         if(check(mid,have)>check(mid2,have))r=mid2;
    26         else l=mid;
    27     }
    28     return max(max(check(l,have),check(r,have)),check(0,have));
    29 }
    30 
    31 int main(){
    32     scanf("%d%d",&n,&m);
    33     a[1].push_back(0),a[2].push_back(0),a[3].push_back(0);
    34     F(i,1,n)
    35     {
    36         int x,y;
    37         scanf("%d%d",&x,&y);
    38         a[x].push_back(y);
    39         sum+=x,ans+=y;
    40     }
    41     if(m>=sum){printf("%lld
    ",ans);return 0;}
    42     ans=0;
    43     sort(a[1].begin(),a[1].end(),greater<int>());
    44     sort(a[2].begin(),a[2].end(),greater<int>());
    45     sort(a[3].begin(),a[3].end(),greater<int>());
    46     F(k,1,3)F(i,0,a[k].size()-1)pre[k][i+1]=pre[k][i]+a[k][i];
    47     F(i,0,a[3].size())if(i*3<=m)ans=max(ans,del(m-i*3)+pre[3][i]);
    48     printf("%lld
    ",ans);
    49     return 0;
    50 }
    View Code

     做法二:

    大范围贪心,小范围DP

    在m比较大的时候,显然选择性价比高的物品比较好,所以按照单位重量的价值排序。

    从前往后选,如果m刚好等于这样选出来的重量,那必然这个答案是最优解。

    如果选择的重量为m-1,m-2,那么这里就要DP一下。

    这里有几种情况,要从已经选择的物品中每一种重量的物品都要挑一个出来。

    这样能为后面的物品腾出空间。

    比如这组数据

    3 6

    1 100

    3 101

    3 101

    如果按照之前的贪心选出来,肯定是选第一个和第二个,此时对剩余容量进行DP。

    如果不将第一个物品挑出来,第三个物品永远也放不进去。

     1 #include<bits/stdc++.h>
     2 #define F(i,a,b) for(int i=a;i<=b;i++)
     3 using namespace std;
     4 typedef long long ll;
     5 typedef pair<int,int> P;
     6 const int N=1e5+7;
     7 
     8 ll ans,dp[N];
     9 int ed,n,m;
    10 struct node
    11 {
    12     int w,c;
    13     double val;
    14     node(int a=0,int b=0):w(a),c(b){}
    15     bool operator<(const node &b)const{return val>b.val;}
    16 }a[N],tmp[N];
    17 
    18 ll DP()
    19 {
    20     F(i,1,ed)for(int j=m;j>=tmp[i].w;j--)
    21         dp[j]=max(dp[j],dp[j-tmp[i].w]+tmp[i].c);
    22     return dp[m];
    23 }
    24 
    25 int main()
    26 {
    27 
    28     scanf("%d%d",&n,&m);ans=0;
    29     F(i,1,n)scanf("%d%d",&a[i].w,&a[i].c),a[i].val=1.0*a[i].c/a[i].w;
    30     sort(a+1,a+1+n);
    31     int tp[4];
    32     memset(tp,-1,sizeof(tp));
    33     F(i,1,n)
    34     {
    35         if(m<30)
    36         {
    37             ed=0;
    38             while(i<=n)tmp[++ed]=a[i],i++;
    39             F(ii,1,3)if(~tp[ii])tmp[++ed]=node(ii,tp[ii]),m+=ii,ans-=tp[ii];
    40             ans+=DP();
    41             break;
    42         }
    43         ans+=a[i].c;
    44         m-=a[i].w;
    45         tp[a[i].w]=a[i].c;
    46     }
    47     printf("%lld
    ",ans);
    48     return 0;
    49 }
    View Code
  • 相关阅读:
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 136 只出现一次的数字
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 135 分发糖果
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 134 加油站
    Java实现 LeetCode 133 克隆图
  • 原文地址:https://www.cnblogs.com/bin-gege/p/6909545.html
Copyright © 2011-2022 走看看