#6039. 「雅礼集训 2017 Day5」珠宝

68 篇文章 0 订阅
47 篇文章 0 订阅

题目描述
Miranda 准备去市里最有名的珠宝展览会,展览会有可以购买珠宝,但可惜的是只能现金支付,Miranda 十分纠结究竟要带多少的现金,假如现金带多了,就会比较危险,假如带少了,看到想买的右买不到。展览中总共有 N 种珠宝,每种珠宝都只有一个,对于第 i 种珠宝,它的售价为 Ci​ 万元,对 Miranda 的吸引力为 Vi​ 。Miranda 总共可以从银行中取出 K万元,现在她想知道,假如她最终带了 i 万元去展览会,她能买到的珠宝对她的吸引力最大可以是多少?
输入格式
第一行两个整数 N、K。
接下来 N行,每行两个整数 Ci、Vi​
输出格式
输出一行 K个整数,对于第 i个数,表示假如 Miranda 带了 i 万元现金,她能买到的珠宝对她的吸引力最大可以是多少

对于 20% 20% 20% 的数据,N,K≤10000 N, K \leq 10000 N,K10000
对于另外 20% 20% 20% 的数据,Ci=Vi C_i = V_i Ci=Vi
对于 100% 100% 100% 的数据,1≤N≤1000000,1≤K≤50000,1≤Ci≤300,0≤Vi≤109 1 \leq N \leq 1000000, 1 \leq K \leq 50000, 1 \leq C_i \leq 300, 0 \leq V_i \leq 10 ^ 9 1N1000000,1K50000,1Ci300,0Vi109

终于把这道初三时的历史遗留问题解决了。。。。。
容易想到c一样的一起转移,发现c一样,v大的应该先选,所以选的一定是最大的那几个,又发现最大的x个的价值在x变大的时候是增长率递减的。。这就是这个dp具有(局部)决策单调性的一个明显标志。。
所以针对每一种c一起用决策单调性的分治dp方法(单调队列也行)转移就可以把这种奇葩的依赖背包问题用klogk的时间复杂度解决,总复杂度O(cklogk)
AC Code:

#include<bits/stdc++.h>
#define maxk 50005
#define LL long long
using namespace std;
 
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &res){ char ch;for(;!isdigit(ch=getc()););for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0'); }
int n,k,siz[305];
vector<LL>sum[305];
LL dp[2][maxk],g[2][maxk];
int now=1,pre=0;
 
void solve(int l,int r,int ql,int qr,vector<LL>&sum,int &siz)
{
    if(ql > qr || l > r) return;
    int Minloc = -1 , mid=(ql+qr)/2;LL val=0;
    for(int i=max(l,mid-siz);i<=r && i<mid;i++)
        if(Minloc==-1||g[0][i]+sum[mid-i-1] > val)
            val = g[0][i]+sum[mid-i-1] , Minloc = i;
    g[1][mid] = val;
    if(Minloc == -1) Minloc = l;
    solve(l,Minloc,ql,mid-1,sum,siz),solve(Minloc,r,mid+1,qr,sum,siz);
}
 
inline bool cmp(const int &u,const int &v){ return u > v; }
 
int main()
{
    read(n),read(k);
    for(int i=1,c,v;i<=n;i++) read(c),read(v),sum[c].push_back(v),siz[c]++;
    for(int i=1;i<=300;i++) 
        if(siz[i]){
            sort(sum[i].begin(),sum[i].end(),cmp);
            for(int j=1;j<siz[i];j++) sum[i][j] += sum[i][j-1];
            for(int j=0;j<i;j++) 
            {
                int p=j,bk=0;
                for(;p<=k;p+=i,bk++)
                    g[0][bk] = dp[pre][p];
                solve(0,bk-1,0,bk-1,sum[i],siz[i]);
                for(p=j,bk=0;p<=k;p+=i,bk++)
                    dp[now][p] = max(g[0][bk] , g[1][bk]);
            }
            swap(now,pre);
        }
    for(int i=1;i<k;i++)
        printf("%lld ",dp[pre][i]);
    printf("%lld\n",dp[pre][k]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值