刺杀 题解
题目简述
你在玩一个游戏,需要刺杀 \(n\) 个敌人。可以肉搏或者用子弹击杀敌人。肉搏第 \(i\) 个敌人会使你的体力值减少 \(x_i\),你要保证你的体力值始终非负。击杀第 \(i\) 个敌人后,会获得 \(y_i\) 颗子弹,有可能 \(y_i\) 为 \(0\),这时候你啥都拿不到。你初始体力值为 \(s\),有一个没有任何子弹的枪。问最多能击杀多少个敌人,以及击杀数最多前提下最少体力消耗。
题目分析
自然想到把 \(y = 0\) 的和 \(y \neq 0\) 的分开来考虑。
如果你用子弹够击杀一个 \(y \neq 0\) 的敌人,你会获得 \(y \geq 1\) 颗子弹,也就是不劣。所以只要能肉搏一个有子弹的敌人,所有有子弹的敌人都可以被杀死。我们把肉搏一个敌人需要消耗的体力称为花费,把获得的子弹称为回报。
所以分为两种情况。第一种情况,不选择有回报的敌人,全部肉搏没有回报的敌人,可以按花费从小到大贪心地打;第二种情况,肉搏杀死最便宜的有回报的敌人,然后全部有回报的敌人都可以死。
但是这样不一定正确。如果有回报的敌人非常的便宜,没有回报的敌人非常的贵,我们会用子弹解决贵的人,尽管没有回报;而那个有回报的敌人,使用肉搏解决。
怎么考虑这个过程?需要肯定的是,当我们拿下最便宜的那个有回报的人,我们总能够拿到 \(\sum y\) 颗子弹,也就是无论如何都能够杀死 \(\sum y\) 个人,所以只需要考虑剩下来的 \(n - \sum y\) 个人的决策,即选择哪些 \(n - \sum y\) 个人使用肉搏?
这就很简单了,问题变成了在剩下 \(n - 1\) 个人中,选择 \(n - \sum y\) 个人肉搏,我们肯定贪心选择花费尽量少的人肉搏。
综合上述两种情况就行了。同时,根据贪心,我们的体力消耗是最优的。
代码
略去了快读快写。
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main() { return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;
#include <algorithm>
int n, s, S, sy;
int x[100010], y[100010];
int A[100010], B[100010];
int a, b;
int ans1, ans2;
signed main() {
fread(buf, 1, MAX, stdin);
read(n), read(s), S = s;
for (int i = 1; i <= n; ++i){
read(x[i]), read(y[i]);
sy += y[i];
if (y[i]) {
A[++a] = x[i];
} else {
B[++b] = x[i];
}
}
sort(A + 1, A + a + 1), sort(B + 1, B + b + 1);
int cnt = 0;
for (int i = 1; i <= b && B[i] <= s; ++i) {
++cnt;
s -= B[i];
}
ans1 = cnt, ans2 = S - s, s = S, cnt = 0;
if (a && s >= A[1]) {
s -= A[1], cnt = 1;
for (int i = 2, j = 1; i <= a || j <= b; ++cnt) {
if (cnt + sy >= n)
break;
if (min(A[i], B[j]) > s)
break;
if (i > a || (j <= b && B[j] < A[i]))
s -= B[j++];
else
s -= A[i++];
}
}
cnt = min(cnt + sy, n);
if (ans1 < cnt) ans1 = cnt, ans2 = S - s;
else if (ans1 == cnt) ans2 = min(ans2, S - s);
printf("%d %d", ans1, ans2);
return 0;
}
后记 & 反思
并没有想到“杀死一个有回报的人,就能拿到所有子弹”这一个结论,也就没有了更深刻的思考。这个问题本质上可以抽象为:
你有若干钱,有若干价值相同的物品,但是花费不同,问你最多能得到多少物品。
在这个问题的基础上进一步包装,就得到了本题。
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18283267。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。