题目描述 乐乐做了一个神奇的梦,他成为了一名刺客。
敌人要经过一片森林,这是刺杀的好机会。敌人共有n个,每个人有一个体力值x和子弹数y,表示如果乐乐徒手打败这个人需要消耗x点体力,打败这个人之后,他就会得到一把含有y颗子弹的手枪(这把手枪可以杀死y个人)。
乐乐刚开始只有s点体力,并且没有手枪(体力消耗后不会恢复,体力不能小于0)。 乐乐刺杀一名敌人,有两种方法:通过体力肉搏或者通过开枪射击。
请你帮乐乐算算,他最多可以刺杀多少敌人和刺杀这些敌人需要消耗的最少体力数。 输入 第一行两个整数,n和s,表示敌人的数量和乐乐最初的体力值。 接下来n行,每行两个整数x和y。
输出 输出两个整数,表示最多的敌人数以及最少的体力消耗
对于30%的数据,n的范围[1,10]; 对于50%的数据,n的范围[1,100];
对于70%的数据,n的范围[1,1000],其中有10%的数据,y的值为0;
对于100%的数据,n的范围[1,10^5],x、s的范围[0,10^9],y的范围[0,10];
刚开始看这到题目感觉是dp,但显然dp的复杂度根本过不了这道题
首先我们可以知道体力是不会恢复的,所以我们可以选择任意顺序去跟人肉搏
即你可以先跟消耗体力值大的打,也可以跟消耗体力值小的人打(只要体力不会降为零)
进一步可以得出,我们若把身上有子弹的人分成一组,那么只要我们把其中体力值最小的人肉搏死,那么这一组的人都可被枪毙。
或许在这里我们会想到把剩下的人体力值大的枪毙,再贪心肉搏体力值小的
表面看起来没有什么问题,但万一有枪的人的x比剩下人的x小,我们肯定要优先肉搏有枪的那个人,然后把子弹留下来枪毙x大的那个人
还有一点需要注意的是,后来枪毙的人不一定一定要被枪毙,可能我们还要把他弹出来肉搏
所以这一动态过程完全可以通过优先队列实现
同时有几个要特判的小点:
1.子弹过多(所有人都可被枪毙)
2.没有子弹
3.直接肉搏可能更好(因为如果要获得子弹,那一定要肉搏有子弹中x最小的那个,但可能x特别大,还不如直接肉搏没子弹的)
#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define M 100005
int A[M];//被gang死的人
int ans,ans1,HP1,cnt,cnt1,HP;
priority_queue<int,vector<int>,greater<int> >q;
int main() {
int n,res=0;//res记录子弹数
scanf("%d%d",&n,&HP);
int tta=HP;HP1=HP;
FOR(i,1,n) {
int x,y;
scanf("%d%d",&x,&y);
if(y){q.push(x);res+=y;cnt1++;}
else A[++cnt]=x;
}
sort(A+1,A+cnt+1);
FOR(i,1,cnt){
if(HP1>=A[i]){
HP1-=A[i];
ans1++;
}
else break;
}
if(!q.empty())HP-=q.top();q.pop();
if(HP<0||res==0){printf("%d %d\n",ans1,tta-HP1);return 0;}
res=res-cnt1+1;//剩余子弹数
if(res>=cnt||HP<0) {printf("%d %d\n",n,tta-HP);return 0;}
ans=res+cnt1;//现在人数(死亡)
FOR(i,cnt-res+1,cnt)q.push(A[i]);
FOR(i,1,cnt-res+1){
if(q.top()<A[i]&&q.top()<=HP){
ans++;
HP-=q.top();
q.pop();
q.push(A[i]);
}
else if(HP>=A[i]){
ans++;
HP-=A[i];
}
}
if(ans<ans1)printf("%d %d\n",ans1,tta-HP1);
else printf("%d %d\n",ans,tta-HP);
return 0;
}