E. Selling Souvenirs
题意:
n件物品,有重量和价值,重量只有三种1,2,3。问取不超过m重量的物品的价值总和最大是多少。(n<=1e5,w<=3e5)
思路:
n*w很大,常规01背包不能直接做。考虑到物品重量不超过3,可以按重量分类物品。枚举重量3的物品取的件数,可以发现如果重量2的物品取得多则重量1的物品就得取得少,反之亦然。因此所取重量1,2的物品的价值和与重量2物品取的件数应该满足一个上凸函数的关系,即有一个极值点,那个点就是当前枚举下的1,2物品最大价值和。因此枚举重量3的物品件数,三分重量2的物品件数可以解决这个问题。
代码:
#include<bits/stdc++.h>
#define dd(x) cout<<#x<<" = "<<x<<" "
#define de(x) cout<<#x<<" = "<<x<<"
"
#define sz(x) int(x.size())
#define All(x) x.begin(),x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
typedef priority_queue<int> BQ;
typedef priority_queue<int,vector<int>,greater<int> > SQ;
const int maxn=1e5+10,mod=1e9+7,INF=0x3f3f3f3f;
vector<int> v[5];
ll sum[5][maxn];
ll f(int w,int n)
{
return sum[2][n]+sum[1][min(sz(v[1]),w-2*n)];
}
int main()
{
int n,m;
cin>>n>>m;
for (int i=0;i<n;++i)
{
int w,c;
scanf("%d%d",&w,&c);
v[w].pb(c);
}
for (int i=1;i<=3;++i)
{
sort(All(v[i]),[&](int a,int b){return a>b;});
for (int j=1;j<=sz(v[i]);++j)
sum[i][j]=sum[i][j-1]+v[i][j-1];
}
ll ans=0;
for (int i=0;i<=sz(v[3])&&3*i<=m;++i)
{
int rest=m-3*i,l=0,r=min(sz(v[2]),rest/2);
ans=max(ans,max(f(rest,l),f(rest,r))+sum[3][i]);
while (r-l>1)
{
int lmid=(l+r)>>1,rmid=(lmid+r)>>1;
if (f(rest,lmid)<f(rest,rmid))
l=lmid;
else
r=rmid;
}
ans=max(ans,max(f(rest,l),f(rest,r))+sum[3][i]);
}
cout<<ans;
return 0;
}