LeetCode/Nim游戏
你和你的朋友,两个人一起玩 Nim 游戏:
桌子上有一堆石头。
你们轮流进行自己的回合,你作为先手 。
每一回合,轮到的人拿掉 1-3块石头。
拿掉最后一块石头的人就是获胜者。
假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true,否则,返回 false 。
1. 博弈
首先考虑到1、2、3是必赢的边界条件
对于其余值,看自己是否必赢,得看对面是否必输
对于评估函数canWinNim(),判断n是否必赢,可以将问题转移
即可以分别选择1、2、3块石头,变成对n-1、n-2、n-3的必输评判
我必然会选择最优解,所以只要n-1、n-2、n-3中有一个为false,表示对面输,所以自己赢
所以会得到canWinNim(n)=!(canWinNim(n-1)&canWinNim(n-2)&canWinNim(n-3))
递归博弈
class Solution {
public:
bool canWinNim(int n) {
if(n<4) return true;
return !(canWinNim(n-1)&canWinNim(n-2)&canWinNim(n-3));
}
};
正向优化
class Solution {
public:
bool canWinNim(int n) {
vector<bool> dp(n+1,1);
for(int i=4;i<n+1;i++)
dp[i]=!(dp[i-1]&dp[i-2]&dp[i-3]);
return dp[n];
}
};
2. 数学规律
可以观察归纳得到规律,4的倍数为输
class Solution {
public:
bool canWinNim(int n) {//123赢 4输 567赢
if(n%4==0) return false;
else return true;
}
};
更简洁
class Solution {
public:
bool canWinNim(int n) {
return n & 3;
}
};