P1049 糖果店 官方题解
Gioush OJ · P1049 糖果店
糖果店
【题意简述】
有 颗糖果,第 颗糖果的价格为 。至多使用 个礼盒,每个礼盒装入两颗糖果,并只支付其中较贵者的价格。给定预算 ,求最多能够购买多少颗糖果。
【数据点 1∼31\sim 31∼3】
【题目描述】
这一档保证 。
【Hint】
每颗糖果有三种状态:不购买、单独结算、装入礼盒。枚举后检查礼盒是否能够两两配对。
【解法】
枚举购买集合,再枚举其中哪些糖果进入礼盒。对于一颗尚未结算的糖果,可以单独购买,也可以选择另一颗尚未结算的糖果与它组成礼盒。
检查总费用是否不超过 ,取所有合法方案中购买数量的最大值。时间复杂度可以做到 ,空间复杂度为 。
【数据点 4∼94\sim 94∼9】
【题目描述】
数据点 保证 ;数据点 保证 。
【Hint】
先固定购买数量 。应当选择哪些糖果?若只能使用一个礼盒,又应当把哪两颗糖果放入礼盒?
【解法】
将价格排序为 。当 时,购买 颗糖果的最小费用为
当 且 时,把所选糖果中最贵的两颗装入礼盒,可以省去其中较便宜者的价格,最小费用为
这一档已经说明:固定购买集合以后,应优先把较贵的糖果放入礼盒。
【数据点 10∼1510\sim 1510∼15】
【题目描述】
这一档允许使用多个礼盒,但 的范围仍允许逐个计算每种购买数量的费用。
【Hint】
固定购买数量 ,令
价格均为正数,因此使用满 个礼盒一定不劣。
【解法】
选择最贵的 颗糖果装入礼盒,再把它们按照价格相邻配对。直接枚举 ,并用 的时间计算费用,总时间复杂度为 。
【正解】
【题目描述】
对每个 求购买恰好 颗糖果的最小费用,并判断它是否不超过 。
【Hint】
证明三件事:
- 只需购买排序后的前 颗糖果;
- 礼盒中装入最贵的 颗糖果;
- 这 颗糖果按照价格相邻配对。
【解法】
任意购买方案中的价格从小到大记为 。全体糖果排序后有 。单独结算与礼盒结算都只会取一个价格或两个价格的最大值,因此把每个 替换成不大的 不会增加费用。故存在一个最优方案,购买的恰好是前 颗糖果。
若某颗较便宜的糖果在礼盒中,而一颗更贵的糖果单独结算,交换二者不会增加费用。因此可以令最贵的 颗糖果全部进入礼盒。
对于 ,相邻配对的费用为 ,另外两种配对的费用均为 。不断消去最小的四颗待配对糖果,即可得到排序后相邻配对最优。
令 。前 颗糖果单独结算,后 颗糖果相邻配对,因此
第二项只取区间 中与 奇偶性相同的位置。分别维护普通前缀和、奇数位置前缀和与偶数位置前缀和,即可在 时间内计算 。
【复杂度分析】
排序的时间复杂度为 ,枚举购买数量的时间复杂度为 ,空间复杂度为 。
【参考代码】
/*Author:EhundateghDate:2026/7/24Name:candy.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 500010using namespace std;int T,n,k,Line[MAXN];long long m,Pre[MAXN],Odd[MAXN],Even[MAXN];long long Calc(int x){ int Count=min(k,x/2),Left=x-Count*2; long long Ret=Pre[Left]; if(x&1) Ret+=Odd[x]-Odd[Left]; else Ret+=Even[x]-Even[Left]; return Ret;}void Solve(){ scanf("%d%lld%d",&n,&m,&k); for(int i=1;i<=n;i++) scanf("%d",&Line[i]); sort(Line+1,Line+n+1); Pre[0]=Odd[0]=Even[0]=0; for(int i=1;i<=n;i++){ Pre[i]=Pre[i-1]+Line[i]; Odd[i]=Odd[i-1];Even[i]=Even[i-1]; if(i&1) Odd[i]+=Line[i]; else Even[i]+=Line[i]; } int Ans=0; for(int i=1;i<=n;i++){ if(Calc(i)<=m) Ans=i; } printf("%d\n",Ans); return;}int main(){ int c; scanf("%d%d",&c,&T); while(T-->0) Solve(); return 0;}