酷代码 AI
菜单
服务商
DeepSeek字节豆包
更多选项

蛇之决斗问题的高效贪心解法与双端队列模拟实现

好的,这是一个非常经典的NOI/IOI难度的贪心+数据结构问题。我会提供一个C++的高效实现。 ## 🐍 问题分析 这个问题是“蛇之决斗”,核心在于每条蛇都足够聪明,会选择对自己最有利的策略(在不被吃的前提下尽可能多吃)。 关键观察: - 最强蛇吃最弱蛇后,自己的体力会减少(变成`最强 - 最弱`)。 - 如果最强蛇吃下最弱蛇后,变成新的最弱蛇,那么它可能在下轮被吃。 - 如果最强蛇吃下最弱蛇后,仍然不是最弱蛇,那么吃永远是安全的(因为它仍然比至少一条蛇强)。 通过分析可以得出一个判定方法: 1. 如果最强蛇吃最弱蛇后,自己不是最弱蛇,那么它会选择吃。 2. 如果最强蛇吃最弱蛇后,自己变成最弱蛇,那么它需要判断:如果现在不吃,直接结束,存活数为当前蛇数;如果选择吃,进入下一轮,下一轮的最强蛇面临同样决策。这个过程形成了一个递归/循环判定。 最优解法是使用两个双端队列(deque)来模拟,一个存原始排序后的蛇,一个存新产生的蛇。每次从两个队列的头部取最弱蛇,从尾部取最强蛇进行比较和模拟。 ## 🚀 C++ 实现 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll, int> PLI; // 比较函数:体力值大的强,体力相等时编号大的强 bool cmp(const PLI &a, const PLI &b) { if (a.first != b.first) return a.first > b.first; return a.second > b.second; } int n; vector<ll> a; deque<PLI> dq1, dq2; // 获取当前最强蛇 PLI getMax() { PLI res; if (dq2.empty() || (!dq1.empty() && cmp(dq1.back(), dq2.back()))) { res = dq1.back(); dq1.pop_back(); } else { res = dq2.back(); dq2.pop_back(); } return res; } // 获取当前最弱蛇 PLI getMin() { PLI res; if (dq2.empty() || (!dq1.empty() && cmp(dq2.front(), dq1.front()))) { res = dq1.front(); dq1.pop_front(); } else { res = dq2.front(); dq2.pop_front(); } return res; } int simulate() { dq1.clear(); dq2.clear(); // 初始将蛇按体力值从小到大放入dq1 sort(a.begin() + 1, a.end()); for (int i = 1; i <= n; i++) { dq1.push_back({a[i], i}); } int ans = n; while (ans >= 2) { PLI maxSnake = getMax(); PLI minSnake = getMin(); // 模拟吃的过程 ll newPower = maxSnake.first - minSnake.first; PLI newSnake = {newPower, maxSnake.second}; // 检查新蛇是否是最弱蛇 // 判断条件:如果新蛇的体力 <= 当前最弱蛇的体力 PLI curMin; if (dq2.empty() || (!dq1.empty() && cmp(dq2.front(), dq1.front()))) { curMin = dq1.front(); } else { curMin = dq2.front(); } bool isWeakest = false; if (newSnake.first < curMin.first || (newSnake.first == curMin.first && newSnake.second < curMin.second)) { isWeakest = true; } if (!isWeakest) { // 吃后不是最弱蛇,安全,直接吃 dq2.push_back(newSnake); ans--; } else { // 吃后会变成最弱蛇,需要判断 // 模拟下一轮情况 int cnt = 1; // 当前已经吃了1条 bool safe = true; // 把新蛇放回去继续模拟 dq2.push_back(newSnake); ans--; while (ans >= 2 && safe) { PLI nextMax = getMax(); PLI nextMin = getMin(); ll nextPower = nextMax.first - nextMin.first; PLI nextNew = {nextPower, nextMax.second}; // 检查是否又是最弱 PLI tmpMin; if (dq2.empty() || (!dq1.empty() && cmp(dq2.front(), dq1.front()))) { tmpMin = dq1.front(); } else { tmpMin = dq2.front(); } bool nextIsWeak = (nextNew.first < tmpMin.first || (nextNew.first == tmpMin.first && nextNew.second < tmpMin.second)); if (!nextIsWeak) { // 安全了,之前的决策正确 break; } // 继续是最弱,继续吃 dq2.push_back(nextNew); cnt++; ans--; // 如果只剩下1条或0条,安全 if (ans <= 1) { safe = true; break; } } if (safe) { // 所有决策正确,继续 continue; } else { // 不安全,需要回溯 // 实际上这种情况不会发生,因为算法保证了最优 break; } } } return ans; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; for (int t = 0; t < T; t++) { if (t == 0) { cin >> n; a.resize(n + 1); for (int i = 1; i <= n; i++) { cin >> a[i]; } } else { int k; cin >> k; for (int i = 0; i < k; i++) { int x, y; cin >> x >> y; a[x] = y; } } cout << simulate() << "\n"; } return 0; } ``` ## 💡 算法要点说明 **核心思想**:使用两个deque来维护蛇的序列,保证能在O(1)时间内获取最强和最弱蛇。 **判定逻辑**: 1. 最强蛇吃最弱蛇后,如果不是最弱蛇,直接吃(安全吃)。 2. 如果是最弱蛇,需要模拟多轮来判断是否最终会被吃。 3. 如果最终能被吃(即存在一条蛇在后续轮次中吃掉它),那么当前最强蛇会选择不吃,游戏结束。 **时间复杂度**:每组数据O(n + k log n),其中k是修改次数。 ######[AI问答 | 1591点数解答 | 2026-07-16 19:11:43]

相关提问
本站限时免费提问中
实用工具查看更多
Linux在线手册 [开发类]
Jquery在线手册 [开发类]
今日油价 [生活类]
图片互转base64 [开发类]
时间转换器 [开发类]