P7078 [CSP-S2020] 贪吃蛇
[CSP-S2020] 贪吃蛇
题目描述
草原上有
接下来这些蛇将进行决斗,决斗将持续若干轮,每一轮实力最强的蛇拥有选择权,可以选择吃或者不吃掉实力最弱的蛇:
- 如果选择吃,那么实力最强的蛇的体力值将减去实力最弱的蛇的体力值,实力最弱的蛇被吃掉,退出接下来的决斗。之后开始下一轮决斗。
- 如果选择不吃,决斗立刻结束。
每条蛇希望在自己不被吃的前提下在决斗中尽可能多吃别的蛇(显然,蛇不会选择吃自己)。
现在假设每条蛇都足够聪明,请你求出决斗结束后会剩几条蛇。
本题有多组数据,对于第一组数据,每条蛇体力会全部由输入给出,之后的每一组数据,会相对于上一组的数据,修改一部分蛇的体力作为新的输入。
输入格式
第一行一个正整数
接下来有
对于第二组到第
第一行第一个非负整数
第二行
输出格式
输出
样例 #1
样例输入 #1
2
3
11 14 14
3
1 5 2 6 3 25
样例输出 #1
3
1
样例 #2
样例输入 #2
2
5
13 31 33 39 42
5
1 7 2 10 3 24 4 48 5 50
样例输出 #2
5
3
样例 #3
样例输入 #3
见附件中的 snakes/snakes3.in
样例输出 #3
见附件中的 snakes/snakes3.ans
样例 #4
样例输入 #4
见附件中的 snakes/snakes4.in
样例输出 #4
见附件中的 snakes/snakes4.ans
提示
【样例 #1 解释】
第一组数据,第一轮中
对于第二组数据,
【数据范围】
对于
对于
对于
对于
对于
Solution
神一样的贪心题。
对于这道题,有一个很强的贪心结论,就是对于最强的蛇,如果吃掉最弱的蛇后不会成为最弱的蛇,那么这条蛇一定会选择吃。
考虑怎么证明这个结论。假设当前最强蛇为
然后来考虑如果
-
条蛇的时候,假设此时的蛇为 ,更强的蛇为 ,那么此时 一定会选择吃,因为吃了就变成最后一条蛇,不会再被吃掉。 -
条蛇的时候,假设多的一条蛇为 ,并且 最强,那么 知道,如果自己吃了 的话,会变成 条蛇的情况,也就是自己一定会被 吃掉,所以为了保证自己不被吃掉, 会选择不吃。 -
条蛇的时候,假设多的一条蛇为 ,并且 最强,那么 也知道,即使自己吃了 , 也不敢吃掉自己,因此 一定会选择吃。 -
可以发现,这里的情况就类似于递归,假设这个函数叫做
由此可以将吃的阶段划分成为两部分:
-
最强的蛇吃了最弱的蛇不会变成最弱的蛇,一直这样吃下去
-
递归确认当前蛇是否应该吃掉最弱的蛇
会发现第一阶段结束过后第二阶段最多只会进行一次(顶多选择吃掉一次,第二条蛇肯定是不敢吃的)。
每次模拟都需要知道当前最强的蛇和最弱的蛇是谁,所以可以用 set
维护一下就可以了,时间复杂度为 O2
可以直接卡着脖子 AC。
#include<bits/stdc++.h>
using namespace std;
constexpr int _SIZE = 1e6;
int n, a[_SIZE + 5], T, k;
set<pair<int, int>> s;
void solve() {
s.clear();
for (int i = 1; i <= n; i++) s.insert(make_pair(a[i], i));
bool type = 0;
int flag, ans;
while (s.size() >= 2) { // 为了写着方便就把两个阶段合并到一起写的,type=1 表示第一阶段,type=2 表示第二阶段
auto weaker = s.begin();
auto stronger = --s.end();
pair<int, int> temp = make_pair(stronger->first - weaker->first, stronger->second);
s.erase(weaker), s.erase(stronger);
s.insert(temp); flag += type;
if (!type && s.begin()->second == temp.second) type = 1, ans = s.size(), flag = 0;
if (type && s.begin()->second != temp.second) break;
}
cout << ans + (flag % 2 == 1) << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> T;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
solve();
for (int times = 2; times <= T; times++) {
cin >> k;
for (int i = 1; i <= k; i++) {
static int x, v;
cin >> x >> v;
a[x] = v;
}
solve();
}
return 0;
}
考虑正解做法,发现 100pts 的数据规模里面写了一个保证数据不降排列,这就在暗示可以使用单调队列来维护最大值和最小值。
定义两个双端队列
进入第二阶段的时候,分
时间复杂度为
#include<bits/stdc++.h>
using namespace std;
constexpr int _SIZE = 1e6;
int n, k, T, a[_SIZE + 5];
void solve() {
deque<pair<int,int>> q1, q2;
for (int i = 1; i <= n; i++) q1.push_back(make_pair(a[i], i));
int ans = 0, flag = 0;
while (q1.size() + q2.size() >= 2) {
auto weaker = q1.front(); q1.pop_front();
auto stronger = q1.back();
if (!q2.empty() && q2.back() > q1.back()) {
stronger = q2.back(); q2.pop_back();
} else q1.pop_back();
pair<int, int> temp = make_pair(stronger.first - weaker.first, stronger.second);
if (temp > q1.front()) q2.push_front(temp);
else {
q1.push_front(temp);
break;
}
}
deque<pair<int, int>> q;
while (!q1.empty() && !q2.empty()) {
if (q1.front() < q2.front()) q.push_back(q1.front()), q1.pop_front();
else q.push_back(q2.front()), q2.pop_front();
}
while (!q1.empty()) q.push_back(q1.front()), q1.pop_front();
while (!q2.empty()) q.push_back(q2.front()), q2.pop_front();
ans = q.size(), flag = 0;
while (q.size() >= 2) {
auto weaker = q.front(); q.pop_front();
auto stronger = q.back(); q.pop_back();
auto temp = make_pair(stronger.first - weaker.first, stronger.second);
flag++;
if (!q.empty() && temp < q.front()) q.push_front(temp);
else break;
}
cout << ans + (flag % 2 == 1) << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> T;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
solve();
for (int i = 2; i <= T; i++) {
cin >> k;
for (int j = 1; j <= k; j++) {
static int x, y;
cin >> x >> y;
a[x] = y;
} solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步