Educational Codeforces Round 21 E - Selling Souvenirs(三分or贪心背包)
题目链接: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\n",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\n",ans); 49 return 0; 50 }
做法二:
大范围贪心,小范围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\n",ans); 48 return 0; 49 }