【贪心+堆优化】兔警官朱迪买礼物

传送门:Vijos(非公开)


这是一道贪心题,主要由2部分组成 1、首先最优情况必定是k张优惠券全部用完,那么先按优惠价排序,排出来前k个优惠价之和如果比m大,则从小到大按优惠价能买几个买几个,然后输出 2、然后考虑如果步骤1后前k个都买下并还有钱剩余,则可能可以再多买,

但是:

前k个必定不能扔掉:一旦某个被扔掉,则只能省下1张优惠券,这张优惠券用于新的物品要花的钱必定比留下那个扔掉的要多 所以要加入新的只有两种可能: (1)从已有的取出一张优惠券用于加入新的物品(此时新的优惠价要最小,并且被取出优惠券的物品优惠价与原价之差要最小) (2)用原价加入新的物品(此时新的原价要最小) 从(1)(2)中取较优状态用于加入 那么为了维护已加入物品优惠价与原价之差最小,可以用堆


以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define MAX 100000000000000007
#define LL long long
priority_queue< LL,vector<LL>,greater<LL> > h;
LL n,m,k,s,a_xb,b_xb,ans;
bool f[100005];
struct price{
LL x,y,z;
bool operator <(const price &s)const{
return y<s.y;
}
}
a[100005],b[100005];
bool cmp(struct price x,struct price y){
return x.x<y.x;
}
int main(){
cin>>n>>m>>k;
for (int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
a[i].z=i;
}
sort(a+1,a+n+1);
for (int i=1;i<=m;i++){
if (s+a[i].y<=k){
s+=a[i].y;
h.push(a[i].x-a[i].y);
}
else{
cout<<i-1;
return 0;
}
}
memcpy(b,a,sizeof b); sort(b+m+1,b+n+1,cmp);
a_xb=m+1; b_xb=m+1;
a[n+1].x=a[n+1].y=MAX; b[n+1].x=b[n+1].y=MAX;
ans=m;
while (a_xb<=n||b_xb<=n){
if (a[a_xb].y+h.top()<b[b_xb].x){
s+=a[a_xb].y+h.top();
h.pop();
h.push(a[a_xb].x-a[a_xb].y);
f[a[a_xb].z]=1;
}
else{
s+=b[b_xb].x;
f[b[b_xb].z]=1;
}
if (s>k){
cout<<ans;
return 0;
}
else ans++;
while (f[a[a_xb].z]&&a_xb<=n) a_xb++;
while (f[b[b_xb].z]&&b_xb<=n) b_xb++;
}
cout<<ans;
return 0;
}