[CF808]Selling Souvenirs

题目

传送门

题解

这道题有两种做法,前者 \(\mathcal O(n)\),后者 \(\mathcal O(n\log_{\frac{3}{2}}n)\) .(此处将 \(n,m\) 视作同阶,不作明显区分)

首先定义 \(a[i][j]\) 为重量为 \(i\) 而价格在重量为 \(i\) 的第 \(j\) 贵的物品.

方法一 —— DP

\(f[i]\) 表示在空间为 \(i\) 时,只选重量为 \(1\)\(2\) 的商品的最大价值,那么我们有初始化 \(f[1]=a[1][1]\)

而后,我们可以每 \(2\) 的跨度为一个单位,每次考虑是选一个 \(2\) 物品还是两个 \(1\) 物品,进行比较之后更新 \(f\) 数组即可

在得到 \(f\) 数组之后,我们用 \(\mathcal O(\frac{m}{3})\) 的复杂度暴力枚举 \(3\) 物品选了多少个,用 \(\mathcal O(1)\) 即可算出价值

方法二 —— 三分查找

同样的思路,首先暴力枚举选了多少个 \(3\) 物品,然后,在重量固定时,以 \(2\) 物品的数量为 \(x\),定义 \(f(x)\) 为价值,可以证明 \(f(x)\) 是单峰的

\(x\) 越大时,选入的 \(2\) 物品价值会越来越小,而剩下的空间来选 \(1\) 物品,一定是尽量往大选,所以,当 \(f(x)\) 下降时,表示再加入的一个 \(2\) 物品价值已经少于扔掉的俩 \(1\) 物品的价格,而扔掉的 \(1\) 物品会随 \(x\) 增加而越来越大,而加入的 \(2\) 物品价值会随 \(x\) 增加而越来越小,所以,当 \(f(x)\) 开始下降之后,就没有可能再增加,即可证明 \(f(x)\) 是单峰的,所以可以使用三分

那么,外圈枚举 \(3\) 物品,对于每一个 \(i\) 做三分,最后标记最大价值即可.

代码

方法一

#include<cstdio>
#include<algorithm>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=100000;
const int MAXM=300000;

int n,m;
int c[4][MAXM+5],sz[4];
LL pre[MAXM+5];//计算重量为 3 的商品的价值前缀和

inline bool cmp(const int x,const int y){return x>y;}
inline void Input(){
    n=read(1),m=read(1);
    int w;
    rep(i,1,n){w=read(1);
        c[w][++sz[w]]=read(1);
    }
    rep(i,1,3){
        sort(c[i]+1,c[i]+sz[i]+1,cmp);
        sz[i]=m;//将少的东西用价值为 0 的补足
    }
    rep(i,1,sz[3])pre[i]=pre[i-1]+c[3][i];
}

LL f[MAXM+5];//在 w=i 时只选重量为 1,2 的商品, 能达到的最大价值为多少
inline void Solvef(){
    f[1]=c[1][1];
    int pos[3][3]={{0},{0,1,1},{0,2,1}};
    int s;
    rep(i,2,m){
        s=(i&1)+1,f[i]=f[i-2];
        if(c[1][pos[s][1]]+c[1][pos[s][1]+1]>c[2][pos[s][2]]){
            f[i]+=0ll+c[1][pos[s][1]]+c[1][pos[s][1]+1];
            pos[s][1]+=2;
        }else{
            f[i]+=0ll+c[2][pos[s][2]];
            ++pos[s][2];
        }
    }
}

inline LL calc(const int x){return pre[x]+f[m-3*x];}
inline void Solve(){
    int l=0,r=Min(sz[3],m/3),p1,p2;
    LL ans=0;
    rep(i,0,Min(sz[3],m/3))ans=Max(ans,calc(i));
    writc(ans,'\n');
}

signed main(){
    Input();
    Solvef();
    Solve();
    return 0;
}
/*
------------------------
6 12
1 10
3 11
3 11
3 11
3 11
3 11

ans == 44
------------------------
6 13
2 18
2 18
2 18
2 18
3 22
3 22

ans == 98
-------------------------
6 9
2 20
2 20
2 20
2 20
3 21
3 21

ans == 81
-------------------------
*/

方法二

#include<cstdio>
#include<algorithm>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=100000;
const int MAXM=300000;

int n,m;
int c[4][MAXM+5],sz[4];
LL pre[4][MAXM+5];//计算重量为 3 的商品的价值前缀和

inline bool cmp(const int x,const int y){return x>y;}
inline void Input(){
    n=read(1),m=read(1);
    int w;
    rep(i,1,n){w=read(1);
        c[w][++sz[w]]=read(1);
    }
    rep(i,1,3){
        sort(c[i]+1,c[i]+sz[i]+1,cmp);
        sz[i]=m;//将少的东西用价值为 0 的补足
    }
    rep(i,1,3)rep(j,1,sz[3])pre[i][j]=pre[i][j-1]+c[i][j];
}

inline LL calc(const int w,const int x){
    return pre[1][w-x*2]+pre[2][x];
}
inline LL Trisearch(const int w){
    int l=0,r=Min(sz[2],w/2),p1,p2;
    while(l+2<r){
        p1=l+(r-l+1)/3,p2=r-(r-l+1)/3;
        if(calc(w,p1)>=calc(w,p2))r=p2;
        else l=p1;
    }
    LL ret=0;
    rep(i,l,r)ret=Max(ret,calc(w,i));
    return ret;
}
inline void Solve(){
    LL ans=0;
    rep(i,0,Min(sz[3],m/3))ans=Max(ans,pre[3][i]+Trisearch(m-3*i));
    writc(ans,'\n');
}

signed main(){
    Input();
    Solve();
    return 0;
}
/*
------------------------
6 12
1 10
3 11
3 11
3 11
3 11
3 11

ans == 44
------------------------
6 13
2 18
2 18
2 18
2 18
3 22
3 22

ans == 98
-------------------------
6 9
2 20
2 20
2 20
2 20
3 21
3 21

ans == 81
-------------------------
*/
posted @ 2020-07-26 11:31  Arextre  阅读(201)  评论(0编辑  收藏  举报