292. Nim 游戏
你和你的朋友,两我的一块儿玩 Nim 游戏:web
桌子上有一堆石头。
大家轮流进行本身的回合,你做为先手。
每一回合,轮到的人拿掉 1 - 3 块石头。
拿掉最后一块石头的人就是获胜者。
假设大家每一步都是最优解。请编写一个函数,来判断你是否能够在给定石头数量为 n 的状况下赢得游戏。若是能够赢,返回 true;不然,返回 false 。svg
示例 1:函数
输入:n = 4
输出:false
解释:若是堆中有 4 块石头,那么你永远不会赢得比赛;
由于不管你拿走 1 块、2 块 仍是 3 块石头,最后一块石头老是会被你的朋友拿走。
示例 2:spa
输入:n = 1
输出:true
示例 3:code
输入:n = 2
输出:truexml
提示:token
1 <= n <= 231 - 1
开始没有想太多,一看就想到动态规划问题
即对于先手来讲,即若是在他的回合只剩下0个石头时他是必败的,若是剩下1~3个石头他是必胜的。
因此当有n个石头时,若是先手对应n个石头,后手则对应n-1或n-2或n-3个石头,则要求先手对应n个石头能不能必赢时,咱们须要知足后手的n-1,n-2,n-3三个方案中有一个必输的方案便可。由于对手是十分聪明的,若是不能在三个中找到使得后手必输的方案,咱们就必定会输。而因为咱们也是十分聪明的,因此咱们也只须要在三个方案中找到一个使得后手必输的就好了,由于后手的三个方案究竟是哪个都是由咱们先手的决定的。游戏
因此不妨设a[n]为表示n个石头时,先手的输赢状况。
因此可列出a[n]=!(a[n-1]==1&&a[n-2]==1&&a[n-3]==1)数学
由于此时已经为n个石头了,因此式子后面的a[n-1]==1&&a[n-2]==1&&a[n-3]==1则为下一个状态了,即由于后手在先手先操做,操做后,他本身也就变成了先手,因此此时能够用来表示后手的输赢状况了。
也能够这样理解:
在n个石头变成n-1或n-2或n-3时已经表明先手拿过了,则此时后手就至关于为n-1或n-2或n-3个石头的先手了。it
可是这样会超时:
bool canWinNim(int n){
bool a[n];
if(n==1||n==2||n==3)
return 1;
a[0]=1;
a[1]=1;
a[2]=1;
for(int i=3;i
{
if(a[i-1]==1&&a[i-2]==1&&a[i-3]==1)
a[i]=0;
else
a[i]=1;
}
return a[n-1];
}
查看题解后发现可直接数学化思考:
即当1,2,3个石头时先手都必赢,4个则必输。当有5 个石头时,咱们发现若开始先手拿1个,则后手也必输,因此能够知道即当谁的回合对应4个石头时谁就必输,由于你们都是聪明人,不论他这回合拿多少,下一我的老是能拿完剩下的全部石头。
而当咱们知道这个“逢四必输”后,咱们能够再看看8个石头,咱们发现当先手拿1~3个石头后,后手老是有一种方案来使得最后给咱们的仍是4个石头,因此咱们仍是必输,因此咱们知道了“逢八必输”,当12个石头时同理咱们先手也会碰到8个石头的状况…因此咱们可知一旦石头为4的倍数咱们老是会因为后手的“聪明操做”而致使面临4的倍数的石头的状况,因此可知代码:
bool canWinNim(int n){
return n%4!=0;
}