[loj6039][雅礼集训 2017 Day5]珠宝——决策单调性+分治

Miranda计划参加珠宝展览会,面临现金携带的难题。在有限的预算内,如何最大化购买珠宝的吸引力成为关键。通过将物品按重量分类并采用贪心算法,结合决策单调性的分治优化,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

###题目大意:
Miranda 准备去市里最有名的珠宝展览会,展览会有可以购买珠宝,但可惜的是只能现金支付,Miranda 十分纠结究竟要带多少的现金,假如现金带多了,就会比较危险,假如带少了,看到想买的右买不到。展览中总共有NNN种珠宝,每种珠宝都只有一个,对于第iii种珠宝,它的售价为CiC_iCi​万元,对 Miranda 的吸引力为ViV_iVi​​ 。
Miranda 总共可以从银行中取出KKK万元,现在她想知道,假如她最终带了iii万元去展览会,她能买到的珠宝对她的吸引力最大可以是多少?
###思路:
每个物品的重量很小,于是按照重量分类,对于同一个重量里面的物品一起考虑。
不难发现,贪心的选择物品,对于同一个重量,选择的物品一定是按照价值从大到小排序的一个前缀。
于是对于每一种重量的决策,体积也按照剩余类分类,不同剩余类的dp分开考虑,此时dp可以看成是连续的。
因为选择的是一个前缀,并且前缀和的斜率不断减小,不难发现满足决策单调性,简单地证明一下:
i&lt;ji&lt;ji<jiii的决策点为ppp,即有任意s&lt;ps&lt;ps<p满足dp[s]+sum[s,i]&lt;dp[p]+sum[p,i]dp[s]+sum[s,i]&lt;dp[p]+sum[p,i]dp[s]+sum[s,i]<dp[p]+sum[p,i],即sum[s,i]−sum[p,i]&lt;dp[p]−dp[s]sum[s,i]-sum[p,i]&lt;dp[p]-dp[s]sum[s,i]sum[p,i]<dp[p]dp[s],又因为有sum[s,j]−sum[p,j]&lt;sum[s,i]−sum[p,i]sum[s,j]-sum[p,j]&lt;sum[s,i]-sum[p,i]sum[s,j]sum[p,j]<sum[s,i]sum[p,i],所以有dp[s]+sum[s,j]&lt;dp[p]+sum[p,j]dp[s]+sum[s,j]&lt;dp[p]+sum[p,j]dp[s]+sum[s,j]<dp[p]+sum[p,j]
于是直接分治优化决策单调性就好了,所谓分治大概就是不断地确定一个区间中点的决策点,这样以后对于左区间和右区间便各自拥有了上界和下界,然后不断地递归就好了。
时间复杂度Θ(300mlog⁡m)\Theta(300m\log{m})Θ(300mlogm)

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long long ll;

using namespace std;

void File(){
	freopen("loj6039.in","r",stdin);
	freopen("loj6039.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=1e6+10;
const int maxm=5e4+10;
const int maxc=300+10;
const ll inf=LLONG_MAX>>1;
int n,m,lst[maxm],tot;
ll dp[maxc][maxm];
vector<ll>cost[maxc];
vector<ll>sum[maxc];

void init(){
	read(n); read(m);
	ll c,v;
	REP(i,1,n)read(c),read(v),cost[c].push_back(v);
	REP(i,1,300)sort(cost[i].begin(),cost[i].end());
	REP(i,1,300){
		ll las=0ll;
		sum[i].push_back(las);
		DREP(j,cost[i].size()-1,0){
			las=las+cost[i][j];
			sum[i].push_back(las);
		}
	}
}

ll calc(int len,int pre,int now){
	int cnt=(now-pre)/len;
	if(cnt>(int)(sum[len].size()-1))return -inf;
	return dp[len-1][pre]+sum[len][cnt];
}

//c_max*m*log(m)
void divide(int len,int l,int r,int L,int R){
	int mid=(l+r)>>1,pos=0; ll Max=0;
	REP(i,L,min(R,mid)){
		ll val=calc(len,lst[i],lst[mid]);
		if(val>Max)Max=val,pos=i;
	}
	if(l==r){dp[len][lst[l]]=Max;return;}
	divide(len,l,mid,L,pos);
	divide(len,mid+1,r,pos,R);
}

void work(){
	REP(i,1,300){
		REP(j,0,i-1){
			lst[tot=1]=j;
			while(lst[tot]+i<=m)lst[tot+1]=lst[tot]+i,++tot;
			divide(i,1,tot,1,tot);
		}
	}
	REP(i,1,m)printf("%lld%c",dp[300][i],i==m ? '\n' : ' ');
}

int main(){
	//File();
	init();
	work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值