最近闲来无事刷LeetCode,发现这道题的Accept Rate还是挺高的,尝试着做了一下,结果悲剧了,把过程写下来,希望能长点记性。该题的描述翻译成中文如下:

你正在和你的朋友玩尼姆游戏(Nim Game): 桌子上有一堆石块,你和你的朋友轮流去拿这些石块,每次只能拿1块、2块或者3块。在石块被拿光前,最后一次拿到石块的人获胜。你将首先去拿这些石块。 你和你的朋友都非常聪明,并且拥有应对该游戏的最佳策略。写一个函数来决定在给定石块数量的情况下,你是否能够获胜。比如:如果桌子上有4块石块,那么你将不可能获胜:不管你先拿出1块、2块还是3块石头,最后一次拿光石块的永远都会是你的朋友。(There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn to remove the stones.Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win the game given the number of stones in the heap.For example, if there are 4 stones in the heap, then you will never win the game: no matter 1, 2, or 3 stones you remove, the last stone will always be removed by your friend.)

拿到题目后,习惯性地点开Hint看了一下:

Hint:

  1. 如果桌子上有5块石头,你能找到确保你会获胜的拿石块的方法吗?(If there are 5 stones in the heap, could you figure out a way to remove the stones such that you will always be the winner?)

第一反应自然是递归了,于是写下了下面的code:

 1  public static boolean canWinNim_recurion(int stoneCount) {
2 if(stoneCount<=0) {
3 System.out.println("illegal input");
4 return false;
5 }
6 /* you will win */
7 if((0<stoneCount)&&(stoneCount<4)) {
8 return true;
9 }
10
11 return !(canWinNim_recurion(stoneCount-1)&&canWinNim_recurion(stoneCount-2)&&canWinNim_recurion(stoneCount-3));
12 }

提交后给出StackOverflowError,自己在本机上用大数(1348820612)试验了一下也是StackOverflowError。原因自然是递归太深了。于是又写下了下面的code:

 1  public static boolean canWinNim(int stoneCount) {
2 if(stoneCount<=0) {
3 System.out.println("illegal input");
4 return false;
5 }
6
7 boolean[] canWinArray = new boolean[stoneCount];
8 for(int i=1;i<=stoneCount;i++) {
9 if(i==1||i==2||i==3) {
10 canWinArray[i-1] = true;
11 continue;
12 }
13 canWinArray[i-1] = !(canWinArray[i-2]&&canWinArray[i-3]&&canWinArray[i-4]);
14 }
15 return canWinArray[stoneCount-1];
16 }

这一次的思路是把递归改成循环,而且为了简单起见,是自底向上的循环。没记错的话这其实是一个DP的解法。但是提交后,给出Memory Limit Exceeded错误,那就是O(n)的空间复杂度不符合要求了。于是给出了下面的空间复杂度为常量的code:

 1  public boolean canWinNim2(int stoneCount) {
2 boolean canWin = false;
3
4 if(stoneCount<=0) {
5 System.out.println("illegal input");
6 return false;
7 }
8
9 /* only need a boolean array of length 3 ? */
10 //boolean[] canWinArray = new boolean[stoneCount];
11 boolean[] last3CanWinArray = new boolean[3];
12
13 for(int i=1;i<=stoneCount;i++) {
14 if(i==1||i==2||i==3) {
15 last3CanWinArray[i-1] = true;
16 canWin = true;
17 continue;
18 }
19 canWin = !(last3CanWinArray[0]&&last3CanWinArray[1]&&last3CanWinArray[2]);
20 // update the array
21 last3CanWinArray[0] = last3CanWinArray[1]; // the index cannot be i-2 i-3 etc.
22 last3CanWinArray[1] = last3CanWinArray[2];
23 last3CanWinArray[2] = canWin;
24 }
25 return canWin;
26 }

但是提交后提示Time Limit Exceeded。这个算法的时间复杂度为O(n),难道还不符合要求吗? 百思不得其解,google了一下,发现这道题的考察意图在找规律而不在写code。然后终于找到了规律:被4整除的石头数,都不可能赢;其他数则能赢。那么代码就太简单了,一行就搞定:

1  public boolean canWinNim_brainteaser(int stoneCount) {
2 return stoneCount%4==0? false:true;
3 }

提交后提示成功,至此算是把这题刷完了。

失败总结:其实,在这道题的描述和Hint后头,有一个tag标签,点开可以看到清清楚楚地写着:brainteaser。也就是脑筋急转弯。而我还老是往DP方面去想,结果自然是悲剧了。刷这道题给了我一个教训:就像高中时刷题一样,对待算法题一定要注意审题,不漏掉题目给出的每一个细节,重视每一个给出的提示,并按照提示给出的方向去思考。

http://www.cnblogs.com/hzg1981/p/5381653.html

一次失败的刷题经历:[LeetCode]292之尼姆游戏(Nim Game)(转)的更多相关文章

  1. 一次失败的刷题经历:[LeetCode]292之尼姆游戏(Nim Game)

    最近闲来无事刷LeetCode,发现这道题的Accept Rate还是挺高的,尝试着做了一下,结果悲剧了,把过程写下来,希望能长点记性.该题的描述翻译成中文如下: 你正在和你的朋友玩尼姆游戏(Nim ...

  2. LeetCode 刷题 App / LeetCode 题解 App

    LeetCode 刷题 APP / LeetCode 题解 App 全端支持 http://leetcode-app.xgqfrms.xyz/ http://leetcode-desktop.xgqf ...

  3. 【刷题】BZOJ 1413 [ZJOI2009]取石子游戏

    Description 在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的: 有n堆石子,将这n堆石子摆成一排.游戏由两个人进行,两人轮流操作,每次操作者都可以从 ...

  4. [LeetCode] Nim Game 尼姆游戏

    You are playing the following Nim Game with your friend: There is a heap of stones on the table, eac ...

  5. leetCode之旅(5)-博弈论中极为经典的尼姆游戏

    题目介绍 You are playing the following Nim Game with your friend: There is a heap of stones on the table ...

  6. C#LeetCode刷题-脑筋急转弯

    脑筋急转弯篇 # 题名 刷题 通过率 难度 292 Nim游戏   62.5% 简单 319 灯泡开关   31.8% 中等 777 在LR字符串中交换相邻字符   21.9% 中等

  7. LeetCode刷题专栏第一篇--思维导图&时间安排

    昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...

  8. LeetCode刷题笔记和想法(C++)

    主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...

  9. 在vscode中配置LeetCode插件,从此愉快地刷题

    大家好,今早在B站看到up主的vscode里藏了leetcode插件,这才知道原来还有这款神器.但是没想到在用的时候遇到了一些麻烦,花了一点时间才解决.所以写这篇文章除了给大家安利这个好用的插件之外, ...

随机推荐

  1. webstorm入门1-主题和配色

    1.引子 以前介绍过 Sublime text 3 系列的文章,着重介绍了 Sublime text 3 如何下载.安装.插件.配置等内容.Sublime text 3的轻量和富扩展,为前端开发带来了 ...

  2. javascript 变量转义

    $(this).append('<a href="2-1partner.html"><div><img width="645" h ...

  3. UVA 10160 Servicing Stations(深搜 + 剪枝)

    Problem D: Servicing stations A company offers personal computers for sale in N towns (3 <= N < ...

  4. Selenium WebDriver java 简单实例

    开发环境 JDK 下载地址: http://www.oracle.com/technetwork/java/javase/downloads/index.html Eclipse: 下载地址:http ...

  5. js之checkbox的代码全选/全不选,使用id获取元素,而不是name

    每当有多个选项的时候,都会有一种想法是:全选,全不选,如果子选项有被选,父选项也得被选. 注意:这里是根据id来获取元素的,但是不能直接用getElementById,因为那只能返回一个,而不是集合. ...

  6. read table 时关键字TRANSPORTING NO FIELDS的用法

    关键字TRANSPORTING NO FIELDS 用于read table with key 一般用于等读取内表的时候,只是判断该内表中是否有次数据 不需要读取到工作区中. READ TABLE g ...

  7. Linux系统管理员必备的监控工具(88款)

    随着互联网行业的不断发展,各种监控工具多得不可胜数.这里列出网上最全的监控工具.让你可以拥有超过80种方式来管理你的机器.在本文中,我们主要包括以下方面: 命令行工具 网络相关内容 系统相关的监控工具 ...

  8. Qt之生成Window资源文件(.rc 文件)

    简述 qmake 可以随意地自动生成一个适当填充的 Windows 资源文件.本节主要讲解如何用 qmake 处理一个 Windows 资源文件,并将其链接到一个可执行应用程序(EXE)或动态链接库( ...

  9. ListCtrl控件着色

    最近在写一款山寨的反病毒软件,大致功能已经实现,还有一些细小的环节需要细化. 其中,在界面编程中,就用到了给ListCtrl控件着色,查看了网上一些文章,终于实现了. 其实说白了,原理很简单,就是Li ...

  10. Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind

    Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind    at ...