1 条题解
-
0
自动搬运
来自洛谷,原作者为

InfiniteHorizon
AFOed | 去见想见的人吧,趁阳光正好,趁微风不噪,趁繁花还未开至荼蘼,趁现在还年轻。搬运于
2025-08-24 21:44:44,当前版本为作者最后更新于2023-07-26 18:36:42,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是反悔贪心的一道好题。
有人问,反悔贪心是什么?
其实贪心本身不带有反悔,是因为此时的贪心可以从局部最优解推出全局最优解。但当有些时候局部最优解推不出全局最优解时,就要用反悔贪心,在适当的时候撤销之前做出的决策。
我们先选取 最小的 个物品使用优惠劵,当前已经使用的价格是 。下文为方便表述,记使用优惠劵的物品集合为 ,他在求解过程中不是固定不变的。
当前考虑第 个物品,由于 张优惠券已经用完了,所以只能以原价 购买物品 。现在考虑反不反悔的条件是什么。
如果要反悔,那么用优惠券买 的价格一定要小于用原价买 。当 用了优惠券,那么 势必要有一个物品(记为 )做出退让,用原价来买 。(其实相当于用 来代替 ),那么一定满足以下不等式:
意思是: 代替 用优惠券的价格比用原价买 便宜,这个时候就需要反悔。
发现 可以消去:
然后把下标相同的归在小于号的同一侧:
他们的形式是相同的,所以我们可以设 :
所以只要在已经使用优惠券的物品里面,存在一个 ,使得 ,我们就需要用 代替 使用优惠券。也就是 个物品中, 。注意这个不是恒等式,因为 ,但是 。
最小值可以用优先队列来求。
话不多说,上代码,代码中用 优先队列存储了上文中提到的 , 存储原价 , 存储优惠价 。
#include<bits/stdc++.h> #define int long long using namespace std; const int maxn=50010; int n,k,m; int p[maxn],c[maxn]; bool buy[maxn];//buy[i] 表示第 i 个物品是否被买过 int ans=0; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >P,C; priority_queue<int,vector<int>,greater<int> >delta; signed main(){ cin>>n>>k>>m; for(int i=1;i<=n;++i){ cin>>p[i]>>c[i]; P.push(make_pair(p[i],i)); C.push(make_pair(c[i],i)); } for(int i=1;i<=k;++i) delta.push(0); while(!P.empty()){ pair<int,int> x1=P.top(); pair<int,int> x2=C.top(); if(buy[x1.second]){//如果被买过了,就跳过 P.pop(); continue; } if(buy[x2.second]){ C.pop(); continue; } if(delta.top() > x1.first-x2.first){//这个式子和上文推的式子时相反的,表示用原价买 i 更划算 m-=x1.first; P.pop(); buy[x1.second]=true; } else{//否则的话,就是用优惠券买 i 更划算 m-=x2.first+delta.top(); delta.pop(); C.pop(); buy[x2.second]=true; delta.push(p[x2.second]-c[x2.second]); } if(m>=0) ans++; else break; } cout<<ans<<endl; return 0; }最后,希望大家都能真正理解返回贪心,它的精髓就是“常思己过”!
静坐常思己过,闲谈莫论人非。——明 · 罗洪先《醒世诗》
- 1
信息
- ID
- 2109
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者