P1050 清仓甩卖 官方题解
Gioush OJ · P1050 清仓甩卖
清仓甩卖
【题意简述】
有 件物品,第 件物品的原价为 ,清仓价格为 。在清仓价格总和不超过 的条件下,求能够得到的最大原价总和。
【数据点 1∼41\sim 41∼4】
【题目描述】
这一档保证 。
【Hint】
直接枚举购买集合。
【解法】
枚举全部 个集合,计算每个集合的清仓价格之和与原价之和,保留总价格不超过 的方案。时间复杂度为 ,空间复杂度为 。
【数据点 5∼125\sim 125∼12】
【题目描述】
数据点 保证所有清仓价格均为 ;数据点 保证所有清仓价格均为 。
【Hint】
当所有物品的清仓价格相同时,只需要决定购买数量。
【解法】
若所有清仓价格均为 ,至多购买 件物品;若均为 ,至多购买 件物品。两种情况下都应选择原价最大的若干件。
将原价从大到小排序后取前若干项之和,时间复杂度为 。
【数据点 13∼1913\sim 1913∼19】
【题目描述】
这一档同时出现两类物品。
【Hint】
分别对清仓价格为 与 的物品排序,再枚举购买多少件第二类物品。
【解法】
枚举购买 件清仓价格为 的物品,剩余预算为 ,再购买至多 件清仓价格为 的物品。
直接累加每次选择的物品价值,时间复杂度为 。当前瓶颈只在重复计算两类物品的前若干项之和。
【正解】
【题目描述】
对每种可能的两类物品数量组合求最大价值。
【Hint】
对两类物品分别维护原价的降序前缀和。
【解法】
将清仓价格为 与 的物品按照原价从大到小排列,分别记为序列 ,并维护前缀和 。
固定购买 件第二类物品时,选择 的前 件一定不劣。此时还能购买
件第一类物品,同样应选择 的前 件。当前方案的价值为
任意合法方案都对应某个 。固定 后,将两类物品分别替换成原价最大的同样件数,不会改变总价格,只会使总价值不减。因此枚举覆盖了某个最优方案。
【复杂度分析】
排序的时间复杂度为 ,枚举的时间复杂度为 ,空间复杂度为 。前缀和与答案需要使用 long long。
【参考代码】
/*Author:EhundateghDate:2026/7/24Name:sale.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 500010using namespace std;int T,n,m,Value[MAXN],Type[MAXN],One[MAXN],Two[MAXN];long long PreOne[MAXN],PreTwo[MAXN];bool cmp(int a,int b){return a>b;}void Solve(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&Value[i]); for(int i=1;i<=n;i++) scanf("%d",&Type[i]); int CntOne=0,CntTwo=0; for(int i=1;i<=n;i++){ if(Type[i]==1) One[++CntOne]=Value[i]; else Two[++CntTwo]=Value[i]; } sort(One+1,One+CntOne+1,cmp); sort(Two+1,Two+CntTwo+1,cmp); PreOne[0]=PreTwo[0]=0; for(int i=1;i<=CntOne;i++) PreOne[i]=PreOne[i-1]+One[i]; for(int i=1;i<=CntTwo;i++) PreTwo[i]=PreTwo[i-1]+Two[i]; long long Ans=0; for(int i=0;i<=CntTwo&&i*2<=m;i++){ int Count=min(CntOne,m-i*2); Ans=max(Ans,PreTwo[i]+PreOne[Count]); } printf("%lld\n",Ans); return;}int main(){ int c; scanf("%d%d",&c,&T); while(T-->0) Solve(); return 0;}