CodeForces 808E Selling Souvenirs(三分法/单调优化dp)

来源:互联网 发布:软件开发很难吗 编辑:程序博客网 时间:2024/06/06 12:42
E. Selling Souvenirs
time limit per test:2 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

After several latest reforms many tourists are planning to visit Berland, and Berland people understood that it's an opportunity to earn money and changed their jobs to attract tourists. Petya, for example, left the IT corporation he had been working for and started to sell souvenirs at the market.

This morning, as usual, Petya will come to the market. Petya has n different souvenirs to sell; ith souvenir is characterised by its weightwi and costci. Petya knows that he might not be able to carry all the souvenirs to the market. So Petya wants to choose a subset of souvenirs such that its total weight is not greater than m, and total cost is maximum possible.

Help Petya to determine maximum possible total cost.

Input

The first line contains two integers n andm (1 ≤ n ≤ 100000,1 ≤ m ≤ 300000) — the number of Petya's souvenirs and total weight that he can carry to the market.

Then n lines follow. ith line contains two integers wi andci (1 ≤ wi ≤ 3,1 ≤ ci ≤ 109) — the weight and the cost ofith souvenir.

Output

Print one number — maximum possible total cost of souvenirs that Petya can carry to the market.

Examples
Input
1 12 1
Output
0
Input
2 21 32 2
Output
3
Input
4 33 102 72 81 1
Output
10



        又是Educational Round,发现还是很训练思维的,决定以后有时间都做做。
        总结了之前的经验,发现对于现在的我们来说,思维思路的训练比高端算法的掌握和熟练更为重要和直接。于是转而将目光投向了CodeForeces,半个星期下来,不说效果显著,但也可以说掌握一些基本的思考方式。
        这题的话,OI选手肯定非常的熟悉,标准的01背包问题,只是数据非常的大,貌似用现有的背包dp方式好像没有什么办法解决。然而,发现物品的质量只有1、2、3,这是一个非常有用的信息。根据背包的质量,我们转移的时候的转移方式就非常的有限。而且,我们可以以各种质量的背包为状态,状态可以用i、j、k来表示,分别表示质量为取3、2、1的数量。当然了,我们没有必要设置一个三位数组来保存状态……然后,对于同一质量的物品,当然要优先取大的,对其进行排序即可,利用单调性优化。然后转移的时候直接搞就行了,也非常的简单。详解见代码:
#include<bits/stdc++.h>using namespace std;struct Node{long long val;int a,b;} dp[310000];vector<int> w1,w2,w3;long long s[110000];int n,m;bool cmp(int a,int b){return a>b;}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);if (x==1) w1.push_back(y);if (x==2) w2.push_back(y);if (x==3) w3.push_back(y);}sort(w1.begin(),w1.end(),cmp);sort(w2.begin(),w2.end(),cmp);sort(w3.begin(),w3.end(),cmp);for(int i=0;i<w3.size();i++)s[i+1]=s[i]+w3[i];dp[0].val=dp[0].a=dp[0].b=0;long long ans=-1e16;for(int i=1;i<=m;i++)//枚举质量为3的物品取的个数{dp[i]=dp[i-1]; int a=dp[i-1].a;if (a<w1.size() && dp[i-1].val+w1[a]>dp[i].val)//取一个质量为1的物品{dp[i].val=dp[i-1].val+w1[a];dp[i].a++;}if (i>1){int b=dp[i-2].b;if (i>1 && b<w2.size() && dp[i-2].val+w2[b]>dp[i].val)//取一个质量为2的物品{dp[i]=dp[i-2]; dp[i].b++;dp[i].val=dp[i-2].val+w2[b];}}ans=max(ans,dp[i].val+s[min((int) w3.size(),(m-dp[i].a-2*dp[i].b)/3)]);//把质量为3的物品的价值加上}ans=max(ans,dp[0].val+s[min((int) w3.size(),(m-dp[0].a-2*dp[0].b)/3)]);printf("%I64d\n",ans);return 0;}


        然而,本文的主角并不是这个dp,这个dp只能说算称之为背包dp的一种。接下来重点讲一下三分法。首先,还是利用它只有三种质量的物品这个性质,同样可以枚举取每种质量物品的个数。最暴力的想法莫过于枚举质量为3和2的物品个数,但是O(n^2)的复杂度不可接受。于是我们就想,我枚举质量为3的物品个数,然后二分质量为2的物品个数可以吗?我们知道,用二分的前提条件是要有单调性,但是,这种情况下并没有,因为当质量为2的物品个数多了,相应的质量为1的物品个数就少了。然而,经过分析,我们发现这个函数很像一个二次函数,即有一个顶点,而且可以证明,函数是先增后减,即含有最大值的。于是,我们就想到了类似二分的方法——三分法。
        其实,三分法我也是很早就听过,但是一直错误的理解他的含义,今天才知道它是解决这类问题的。具体来说还是得根据图像来理解
        如图,我们根据左右端点确定一个lmid,然后再根据lmid和r确定rmid,比较lmid和rmid两点的函数值,由图可分成两种情况:当f(lmid)>f(rmid)时,结果肯定在l和rmid之间;当f(lmid)<f(rmid)的时候,结果肯定在lmid和r之间,然后利用这个不断三分逼近ans就可以得到结果。可以看到,这个和二分法其实很类似,不同的地方就在单调性不同,三分法几乎可以使用与所有的连续函数,当然前提是限定区间内只有一个极值。
        然后个人感觉这题和2017银河之光校赛的G题有点想,只不过那题是由两个单调函数组成的分段的函数,用二分找交点即可。三分法具体代码:
#include<bits/stdc++.h>using namespace std;long long s[110000][3];vector<int> w[3];int n,m,i;bool cmp(int a,int b){return a>b;}long long f(int x){return s[x][1]+s[min(m-i*3-x*2,(int) w[0].size())][0];}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int x,y;scanf("%d%d",&x,&y);w[x-1].push_back(y);}long long ans=0;sort(w[0].begin(),w[0].end(),cmp);sort(w[1].begin(),w[1].end(),cmp);sort(w[2].begin(),w[2].end(),cmp);for(int i=0;i<3;i++)for(int j=0;j<w[i].size();j++)s[j+1][i]=s[j][i]+w[i][j];int l,r,lmid,rmid;for(i=0;i<=min((int) w[2].size(),m/3);i++){l=0,r=min((m-i*3)/2,(int) w[1].size());while (l<r-1){lmid=(l+r)>>1;rmid=(lmid+r)>>1;if (f(lmid)>f(rmid)) r=rmid;                else l=lmid;}ans=max(ans,max(f(l),f(r))+s[i][2]);}printf("%I64d\n",ans);return 0;}


原创粉丝点击