棋盘博弈

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. 完全占领左半部分(第 1 行和第 2 行的前 mid 列):

    Mandy = pre[0][mid] + pre[1][mid]
    
  2. 如果 Mandy 继续向右推进,则可能拿到:

    • 第 1 行全部(pre[0][n])
    • 或者第 2 行前 mid + 1 列 + 第 1 行前 mid + 1 列

    取两者较小值(因为 brz 也会抢):

    min(pre[0][n], pre[1][mid + 1] + pre[0][mid + 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 胜

三、算法流程

  1. 读入 n 和两行分数
  2. 分别对两行求前缀和
  3. 计算 mid = n / 2
  4. 计算 Mandy 的得分 num
  5. 与总分比较,输出结果

四、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],轮到先手。

先手有两种走法:

  1. 往右走:走到 arr[0][mid + 1],之后走完第一行。(被后手限制)
  2. 往下走:走到 arr[1][mid],走 pre[0][mid] 和 pre[1][mid] (不管咋样,这种都能走到)

选择 2:往上走一步

后手走到 arr[0][mid],轮到先手。

  1. 先手最优走法:走完 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 与总分比较
posted @ 2026-09-26 12:07  leaderling  阅读(2)  评论(0)    收藏  举报