棋盘博弈
Mandy 和 brz 的棋盘游戏
一、题目描述
在一个 2 × n 的棋盘上,每个格子有一个分数 a[i][j]。
- Mandy 的棋子从左上角
(1, 1)出发 - brz 的棋子从右下角
(2, n)出发 - 两人轮流操作,Mandy 先手
- 每次操作可以把自己的棋子移到相邻格子(上下左右),或者不动
- 移动的目标格子必须有分数,不能移动或不动则跳过
- 移动后,目标格子的分数被该玩家获得,格子分数消失
- 双方都无法移动时游戏结束,总分高者获胜,平局输出
draw
双方都采用最优策略,问最终胜负。
对应题目:CCPC 2025 河北省赛 A.棋盘
二、思路分析
观察棋盘结构
棋盘只有两行,Mandy 从左上角出发,brz 从右下角出发。
两人最终相遇的位置只可能在中间区域,所以可以按「中间分割线」把棋盘分成左右两部分。
关键观察
设 mid = n / 2,那么:
- Mandy 从左边开始,一定会先吃掉左半部分的分数
- brz 从右边开始,会吃掉右半部分的分数
- 最终的分界线在中间附近
双方的得分
Mandy 的得分有两种可能:
-
完全占领左半部分(第 1 行和第 2 行的前
mid列):Mandy = pre[0][mid] + pre[1][mid] -
如果 Mandy 继续向右推进,则可能拿到:
- 第 1 行全部(
pre[0][n]) - 或者第 2 行前
mid + 1列 + 第 1 行前mid + 1列
取两者较小值(因为 brz 也会抢):
min(pre[0][n], pre[1][mid + 1] + pre[0][mid + 1]) - 第 1 行全部(
Mandy 最终得分:两种情况取较大值:
num = max(min(pre[0][n], pre[1][mid + 1] + pre[0][mid + 1]), num);
比较总分
总分为:
total = pre[0][n] + pre[1][n]
- 若
num * 2 > total→ Mandy 胜 - 若
num * 2 == total→ 平局 - 若
num * 2 < total→ brz 胜
三、算法流程
- 读入
n和两行分数 - 分别对两行求前缀和
- 计算
mid = n / 2 - 计算 Mandy 的得分
num - 与总分比较,输出结果
四、AC 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
ll arr[2][N];
ll pre[2][N];
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[0][i];
pre[0][i] = pre[0][i - 1] + arr[0][i];
}
for (int i = 1; i <= n; i++)
{
cin >> arr[1][i];
pre[1][i] = pre[1][i - 1] + arr[1][i];
}
int mid = n >> 1;
ll num = pre[0][mid] + pre[1][mid];
num = max(min(pre[0][n], pre[1][mid + 1] + pre[0][mid + 1]), num);
if (num * 2 > pre[0][n] + pre[1][n]) cout << "Mandy" << endl;
else if (num * 2 == pre[0][n] + pre[1][n]) cout << "draw" << endl;
else cout << "brz" << endl;
}
return 0;
}
五、代码说明
1. 前缀和
pre[0][i] = pre[0][i - 1] + arr[0][i];
pre[1][i] = pre[1][i - 1] + arr[1][i];
用前缀和可以 O(1) 求出任意区间和。
2. 中间分界
int mid = n >> 1;
取 n / 2 作为中间位置。
3. Mandy 的得分
ll num = pre[0][mid] + pre[1][mid];
num = max(min(pre[0][n], pre[1][mid + 1] + pre[0][mid + 1]), num);
- 第一种:Mandy 吃掉前
mid列的所有分数 - 第二种:Mandy 尽量向右抢,但要考虑 brz 的阻挡,取较小值
- 两种情况取最大,就是 Mandy 的最优得分
4. 胜负判断
if (num * 2 > pre[0][n] + pre[1][n]) cout << "Mandy" << endl;
else if (num * 2 == pre[0][n] + pre[1][n]) cout << "draw" << endl;
else cout << "brz" << endl;
- 用
num * 2和总分比较,避免浮点误差 - 大于总分 → Mandy 胜
- 等于总分 → 平局
- 小于总分 → brz 胜
六、错误代码分析
if (n % 2 == 0)
{
ll num = pre[0][mid] + pre[1][mid];
if (n == 2) num = max(num, pre[0][2]);
if (num * 2 > pre[0][n] + pre[1][n]) cout << "Mandy" << endl;
else if (num * 2 == pre[0][n] + pre[1][n]) cout << "draw" << endl;
else cout << "brz" << endl;
}
- 反例
| 1 | 2 | mid | mid+1 | mid+2 | n |
|---|---|---|---|---|---|
| 1 | 100 | 100 | 30 | 1000 | 1 |
| 1 | 2 | 1 | 13 | 4 | 1 |
解释:以 (n) 为偶数为例,一开始两人的最优策略都一定是向中间推进。
- 先手到
arr[0][mid],后手到arr[1][mid + 2]
此时后手有两种选择:
选择 1:往左走一步
后手走到 arr[1][mid + 1],轮到先手。
先手有两种走法:
- 往右走:走到
arr[0][mid + 1],之后走完第一行。(被后手限制) - 往下走:走到
arr[1][mid],走pre[0][mid]和pre[1][mid](不管咋样,这种都能走到)
选择 2:往上走一步
后手走到
arr[0][mid],轮到先手。
- 先手最优走法:走完
pre[0][mid + 1],即pre[0][mid + 1] + pre[1][mid + 1]。(被后手限制)
后手会选择最优于自己不利于先手的方案,此时先手在1, 3中只能选择小的那个
七、总结
| 要点 | 内容 |
|---|---|
| 核心思想 | 棋盘中间分界,Mandy 取左半部分,考虑向右推进的可能 |
| 数据结构 | 前缀和 |
| 关键公式 | num = max(pre[0][mid] + pre[1][mid], min(pre[0][n], pre[1][mid+1] + pre[0][mid+1])) |
| 判断 | num * 2 与总分比较 |

浙公网安备 33010602011771号