专栏文章
题解:AT_agc026_f [AGC026F] Manju Game
AT_agc026_f题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minid67f
- 此快照首次捕获于
- 2025/12/02 02:54 3 个月前
- 此快照最后确认于
- 2025/12/02 02:54 3 个月前
作者撰写的内容标识:本题解除了提议理解相关内容(这部分由 AI 撰写)均为作者撰写。
一、题意简述
# 游戏规则
有 个盒子从左到右排成一排,第 个盒子中有 个馒头。Sugim(先手)和 Sigma(后手)进行博弈游戏。
游戏规则:
- 两人轮流操作,每次操作选择一个未放置棋子的盒子放入自己的棋子。
- 相邻限制:必须选择与对手上一次操作相邻的盒子(左右相邻均可)。
- 特殊情况:
- 如果没有满足条件的相邻盒子,可以任选一个未放置的盒子。
- Sugim 的第一次操作可以任选盒子。
- 游戏进行 次后结束,每人获得自己棋子所在盒子的所有馒头。
目标:求双方都采用最优策略时,各自能获得多少馒头。
二、核心观察
# 观察 1:游戏的奇偶性质
关键发现:无论如何游戏,最终被选择的位置具有奇偶交替的性质。
证明思路:
- Sugim 第一步选择位置 。
- Sigma 必须选择 或 (相邻位置)。
- 之后每步都必须选择与上一步相邻的位置。
- 因此形成的序列必然是连续的,相邻位置的下标奇偶性不同。
推论:
- 如果 为偶数,必然有一方取所有奇数位置,另一方取所有偶数位置。
- 如果 为奇数,会在某处发生"转折",改变奇偶归属。
# 观察 2: 为偶数时的简单性
当 为偶数时:
- 设 ,。
- 由于 Sugim 先手,他可以选择取所有奇数位置或所有偶数位置。
- 最优策略:Sugim 取 ,Sigma 取 。
三、 为奇数时的解法
# 3.1 问题建模
对于 为奇数的情况,我们需要更精细的分析。
差值序列的构造:
定义前缀差值序列 :
其中 。
物理意义:
- 表示前 个位置按照"奇数位给 Sugim,偶数位给 Sigma"分配时,Sugim 相对于 Sigma 的优势值。
- 如果存在"转折点",则某些偶数位置会被 Sugim 获得,奇数位置被 Sigma 获得。
# 3.2 二分答案
目标:找到 Sugim 相对于平均值的最大优势 。
最终答案:
- Sugim 得分
- Sigma 得分
二分范围:。
二分策略:
- 如果能达到优势 ,尝试更大的 。
- 否则缩小 。
# 3.3 Check 函数详解
问题:如何判断是否能让 Sugim 获得至少 的优势?
贪心 DP 思路:
CPPbool check(int delta) {
int mn = 0; // 记录可用的最小前缀和
for (int i = 1; i <= n; i += 2) {
if (s[i] - mn >= delta) // 如果在位置 i 能达到优势 delta
mn = min(mn, s[i + 1]); // 更新最小前缀,允许在 i+1 处"转折"
}
return s[n] - mn >= delta; // 最终检查是否达到 delta
}
算法原理:
- 状态定义:
mn表示当前可选的最优"起始前缀和"。 - 转折点选择:
- 遍历所有奇数位置 。
- 如果 ,说明从某个"起始点"到 能达到优势 。
- 此时可以选择在 处"转折",即改变后续的奇偶归属。
- 更新 ,为后续提供更优的起始点。
- 最终判定:检查 。
为什么这样是正确的?
- 游戏中可以有多次"转折"(重新选择不相邻的盒子)。
- 每次转折相当于选择一个新的起始点。
- 贪心地选择最优起始点(前缀和最小)可以最大化后续优势。
- DP 本质:
dp[i] = true表示能在位置 达到优势 。
四、算法流程
# 完整步骤
- 输入处理:
- 读入 和数组 。
- 计算 和 。
- 构造差值序列 。
- 为偶数时:
- 如果 ,直接输出 和 。
- 为奇数时:
- 二分答案 。
- 对每个 ,调用
check(delta)验证可行性。 - 找到最大的可行 。
- 输出 和 。
五、代码实现
CPP#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fclose fclose(stdin), fclose(stdout)
#define debug(x) cerr << #x << " = " << (x) << endl
int n;
vector<int> a, s;
int odd, even;
bool check(int delta) {
int mn = 0;
for (int i = 1; i <= n; i += 2) {
if (s[i] - mn >= delta)
mn = min(mn, s[i + 1]);
}
return s[n] - mn >= delta;
}
void solve() {
cin >> n;
a.resize(n + 5);
s.resize(n + 5);
odd = even = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (i & 1) {
odd += a[i];
s[i] = s[i - 1] + a[i];
} else {
even += a[i];
s[i] = s[i - 1] - a[i];
}
}
if (!(n & 1)) {
cout << max(odd, even) << " " << min(odd, even) << endl;
return;
}
int l = 0, r = odd + even, delta = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
delta = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << even + delta << " " << odd - delta << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
//cin >> T;
while (T--) {
solve();
}
return 0;
}
六、复杂度分析
# 时间复杂度
- 构造差值序列:。
- 二分答案:,其中 。
- 每次
check:。 - 总复杂度:。
# 空间复杂度
- 数组存储:。
- 总复杂度:。
七、样例详解
# 样例 1
输入:
CPP5
20 100 10 1 10
分析:
- (奇数)。
- 。
- 。
- 差值序列:
- 二分过程:
- 初始:
- 二分得到最大可行
- 最终答案:
- Sugim
- Sigma
输出:
120 21# 样例 2
输入:
CPP6
4 5 1 1 4 5
分析:
- (偶数)。
- 。
- 。
- 直接输出:,。
输出:
11 9八、关键要点总结
# 核心技巧
- 奇偶性质:利用游戏的相邻限制,发现位置的奇偶交替规律。
- 差值序列:将问题转化为优势值的计算。
- 二分答案:将最优化问题转化为判定问题。
- 贪心 DP:通过维护最优前缀和,判断是否能达到目标优势。
# 易错点
- 边界处理:注意 的访问,需要预留足够空间。
- 奇偶判断:注意区分 的奇偶性。
- 二分边界: 的上界是 ,不是 。
# 推广
这道题的思想可以推广到其他相邻限制的博弈问题,核心是:
- 发现游戏的结构性质(如奇偶性)。
- 用差值序列建模优势关系。
- 二分 + 贪心验证可行性。
九、参考题目难度及算法标签(仅代表个人观点)
- 本题涉及的知识点:博弈论、二分答案、贪心、动态规划。
- 难度评级:省选 / NOI-(个人观点,认为应该是个中紫)。
作者原创内容:核心观察、算法详解、代码实现、技巧总结、以及其他解题核心部分。
由 AI 辅助 / 生成的内容大致包括但不限于:题解格式、代码细节讲解、题解思路框架、以及样例分析的具体内容(在作者写完过后提交给 AI 检查过一遍拼写等细节问题)。
附录、Updates
- 2025/11/14:
- 数学公式规范化
- 移除公式中的中文,使用数学符号表达条件
- 集合求和使用集合符号:
- 条件判断使用数学表达式: 代替 "if 为奇数"
- 中英文间距
- 在所有中文与英文、数字、公式之间添加了半角空格
- 例如:"有个盒子" → "有 个盒子"
- 标点符号统一
- 全文统一使用中文标点符号
- 确保每个句子都有句号结尾
- 术语规范化
- "奇数情况" → " 为奇数时"
- "偶数情况" → " 为偶数时"
- 数学公式规范化
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...