Codeforces 808 E. Selling Souvenirs(三分)

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<<"\n"
#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;
}

猜你喜欢

转载自www.cnblogs.com/orangee/p/10328414.html