「博弈dp」棋盘游戏

本题为12月30日22寒假集训每日一题题解

题目来源:(未知)

题面

题目描述

懒羊羊和小灰灰在玩一种棋盘游戏,棋盘的尺寸为n个方格*m个方格。一开始在棋盘的右上角(1,m)放一枚硬币,每次一个人可以将硬币向左、下或左下的方格移动。

当某个人无法再移动硬币了,那么这个人就输了。游戏总是懒羊羊先开始,如果他们两个每步都是最优策略,则谁将赢得游戏?

输入

输入包含多组测试数据。每组输入两个整数n和m(0< n, m <=2000)。
当n = m = 0时,输入结束。

输出

对于每组输入,如果懒羊羊赢,输出“Wonderful!”,否则输出“What a pity!”。

样例输入 Copy

5 3
5 4
6 6
0 0

样例输出 Copy

What a pity!
Wonderful!
Wonderful!

思路分析

显然此题直接按照博弈论的基础做法——动态规划即可完成.

在博弈论问题中,状态即为当前规模问题的胜负情况(每次每方操作,都会缩小问题的规模,同时交换先后手),状态转移方程即为游戏的规则,不过由于先后手交换需要取反.由于先手无法移动的状态只可能是左下角的那个格子,所以我们可以将其定义为初始状态,然后从这个状态开始,反推回右上角的格子即可.右上角格子的状态即为当前游戏的结果是胜是负.这里注意是多组输入,每组输入dp数组需要重新初始化.

参考代码

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

bool dp[2010][2010] = {0}; // 状态转移用数组,记录每个状态先手的胜负情况

int main()
{
    ios::sync_with_stdio(false);

    int n, m;

    while (cin >> n >> m && n != 0)
    {
        memset(dp, 0, sizeof(dp)); // 重置dp数组

        // 从左下角的格子开始状态转移
        for (int i = n - 1; i >= 0; i--)
        {
            for (int j = 0; j < m; j++)
            {
                // 当前状态能由其左、下、左下三个状态转移得到,只要一个状态为真,当前状态即为真.注意每次转移由于先后手转换,需要取反,同时要验证是否越界
                if (i < n - 1)
                {
                    dp[i][j] |= !dp[i + 1][j];
                }
                if (j > 0)
                {
                    dp[i][j] |= !dp[i][j - 1];
                }
                if (i < n - 1 && j > 0)
                {
                    dp[i][j] |= !dp[i + 1][j - 1];
                }
            }
        }

        if (dp[0][m - 1]) // 右上角的初始状态即代表当前游戏是胜是负
        {
            cout << "Wonderful!\n";
        }
        else
        {
            cout << "What a pity!\n";
        }
    }

    return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

posted @ 2022-12-30 18:39  星双子  阅读(128)  评论(0编辑  收藏  举报