刺杀 题解

题目简述

你在玩一个游戏,需要刺杀 \(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;
}

后记 & 反思

并没有想到“杀死一个有回报的人,就能拿到所有子弹”这一个结论,也就没有了更深刻的思考。这个问题本质上可以抽象为:

你有若干钱,有若干价值相同的物品,但是花费不同,问你最多能得到多少物品。

在这个问题的基础上进一步包装,就得到了本题。

posted @ 2024-07-04 11:20  XuYueming  阅读(7)  评论(0编辑  收藏  举报