「博弈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;
}
"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德
这里是浙江理工大学22届ACM集训队的成员一枚鸭!
本文首发于博客园,作者:星双子,除了我自己的转载请注明原文链接:https://www.cnblogs.com/geministar/p/zstu22_12_30.html