Alex and Lee play a game with piles of stones.  There are an even number of piles arranged in a row, and each pile has a positive integer number of stones `piles[i]`.

The objective of the game is to end with the most stones.  The total number of stones is odd, so there are no ties.

Alex and Lee take turns, with Alex starting first.  Each turn, a player takes the entire pile of stones from either the beginning or the end of the row.  This continues until there are no more piles left, at which point the person with the most stones wins.

Assuming Alex and Lee play optimally, return True if and only if Alex wins the game.

Example 1:

Input: [5,3,4,5]
Output: true
Explanation:
Alex starts first, and can only take the first 5 or the last 5.
Say he takes the first 5, so that the row becomes [3, 4, 5].
If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points.
If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points.
This demonstrated that taking the first 5 was a winning move for Alex, so we return true.

Note:

  1. 2 <= piles.length <= 500
  2. piles.length is even.
  3. 1 <= piles[i] <= 500
  4. sum(piles) is odd.

这道题说是有偶数堆的石子,每堆的石子个数可能不同,但石子总数是奇数个。现在 Alex 和 Lee (不应该是 Alice 和 Bob 么??)两个人轮流选石子堆,规则是每次只能选开头和末尾中的一堆,最终获得石子总数多的人获胜。若 Alex 先选,两个人都会一直做最优选择,问我们最终 Alex 是否能获胜。博主最先想到的方法是像 [Predict the Winner](http://www.cnblogs.com/grandyang/p/6369688.html) 中的那样,用个 player 变量来记录当前是哪个玩家在操作,若为0,表示 Alex 在选,那么他只有两种选择,要么拿首堆,要么拿尾堆,两种情况分别调用递归,两个递归函数只要有一个能返回 true,则表示 Alex 可以获胜,还需要用个变量 cur0 来记录当前 Alex 的石子总数。同理,若 Lee 在选,即 player 为1的时候,也是只有两种选择,分别调用递归,两个递归函数只要有一个能返回 true,则表示 Lee 可以获胜,用 cur1 来记录当前 Lee 的石子总数。需要注意的是,当首堆或尾堆被选走了后,我们需要标记,这里就有两种方法,一种是从原 piles 中删除选走的堆(或者是新建一个不包含选走堆的数组),但是这种方法会包括大量的拷贝运算,无法通过 OJ。另一种方法是用两个指针 left 和 right,分别指向首尾的位置。当选取了首堆时,则 left 自增1,若选了尾堆时,则 right 自减1。这样就不用执行删除操作,或是拷贝数组了,大大的提高了运行效率,参见代码如下:


解法一:

class Solution {
public:
bool stoneGame(vector<int>& piles) {
return helper(piles, 0, 0, 0, (int)piles.size() - 1, 0);
}
bool helper(vector<int>& piles, int cur0, int cur1, int left, int right, int player) {
if (left > right) return cur0 > cur1;
if (player == 0) {
return helper(piles, cur0 + piles[left], cur1, left + 1, right, 1) || helper(piles, cur0 + piles[right], cur1, left + 1, right, 1);
} else {
return helper(piles, cur0, cur1 + piles[left], left, right - 1, 0) || helper(piles, cur0, cur1 + piles[right], left, right - 1, 0);
}
}
};

这道题也可以使用动态规划 Dynamic Programming 来做,由于玩家获胜的规则是拿到的石子数多,那么多的石子数就可以量化为 dp 值。所以我们用一个二维数组,其中 dp[i][j] 表示在区间 [i, j] 内 Alex 比 Lee 多拿的石子数,若为正数,说明 Alex 拿得多,若为负数,则表示 Lee 拿得多。则最终只要看 dp[0][n-1] 的值,若为正数,则 Alex 能获胜。现在就要找状态转移方程了,我们想,在区间 [i, j] 内要计算 Alex 比 Lee 多拿的石子数,在这个区间内,Alex 只能拿i或者j位置上的石子,那么当 Alex 拿了 piles[i] 的话,等于 Alex 多了 piles[i] 个石子,此时区间缩小成了 [i+1, j],此时应该 Lee 拿了,此时根据我们以往的 DP 经验,应该调用子区间的 dp 值,没错,但这里 dp[i+1][j] 表示是在区间 [i+1, j] 内 Alex 多拿的石子数,但是若区间 [i+1, j] 内 Lee 先拿的话,其多拿的石子数也应该是 dp[i+1][j],因为两个人都要最优化拿,那么 dp[i][j] 的值其实可以被 piles[i] - dp[i+1][j] 更新,因为 Alex 拿了 piles[i],减去 Lee 多出的 dp[i+1][j],就是区间 [i, j] 中 Alex 多拿的石子数。同理,假如 Alex 先拿 piles[j],那么就用 piles[j] - dp[i][j-1] 来更新 dp[i][j],则我们用二者的较大值来更新即可。注意开始的时候要把 dp[i][i] 都初始化为 piles[i],还需要注意的是,这里的更新顺序很重要,是从小区间开始更新,在之前那道 [Burst Balloons](http://www.cnblogs.com/grandyang/p/5006441.html),博主详细的讲了这种 dp 的更新顺序,可以去看看,参见代码如下:


解法二:

class Solution {
public:
bool stoneGame(vector<int>& piles) {
int n = piles.size();
vector<vector<int>> dp(n, vector<int>(n));
for (int i = 0; i < n; ++i) dp[i][i] = piles[i];
for (int len = 1; len < n; ++len) {
for (int i = 0; i < n - len; ++i) {
int j = i + len;
dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);
}
}
return dp[0][n - 1] > 0;
}
};

其实这道题是一道脑筋急转弯题,跟之前那道 [Nim Game](http://www.cnblogs.com/grandyang/p/4873248.html) 有些像。原因就在于题目中的一个条件,那就是总共有偶数堆,那么就是可以分为堆数相等的两堆,比如我们按奇偶分为两堆。题目还说了石子总数为奇数个,那么分出的这两堆的石子总数一定是不相等的,那么我们只要每次一直取石子总数多的奇数堆或者偶数堆,Alex 就一定可以躺赢,所以最叼的方法就是直接返回 true。我。。我。。我王司徒。。表示不服。。。我从未见过如此。。机智过人。。的解法~


解法三:

class Solution {
public:
bool stoneGame(vector<int>& piles) {
return true;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/877

类似题目:

Burst Balloons

Nim Game

Flip Game II

Guess Number Higher or Lower II

Predict the Winner

参考资料:

https://leetcode.com/problems/stone-game/

https://leetcode.com/problems/stone-game/discuss/154610/DP-or-Just-return-true

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 877. Stone Game 石子游戏的更多相关文章

  1. leetcode 877. Stone Game 详解 -——动态规划

    原博客地址 https://blog.csdn.net/androidchanhao/article/details/81271077 题目链接 https://leetcode.com/proble ...

  2. LeetCode 877. Stone Game

    原题链接在这里:https://leetcode.com/problems/stone-game/ 题目: Alex and Lee play a game with piles of stones. ...

  3. [LeetCode] 877. Stone Game == [LintCode] 396. Coins in a Line 3_hard tag: 区间Dynamic Programming, 博弈

    Alex and Lee play a game with piles of stones.  There are an even number of piles arranged in a row, ...

  4. Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game)

    Leetcode之动态规划(DP)专题-877. 石子游戏(Stone Game) 亚历克斯和李用几堆石子在做游戏.偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] . 游戏以谁手中的石子最 ...

  5. 877. Stone Game - LeetCode

    Question 877. Stone Game Solution 题目大意: 说有偶数个数字,alex和lee两个人比赛,每次轮流从第一个数字或最后一个数字中拿走一个(偶数个数字,所以他俩拿的数字个 ...

  6. bzoj2000 [Hnoi2010]stone 取石头游戏

    Description A 公司正在举办一个智力双人游戏比赛----取石子游戏,游戏的获胜者将会获得 A 公司提供的丰厚奖金,因此吸引了来自全国各地的许多聪明的选手前来参加比赛. 与经典的取石子游戏相 ...

  7. LeetCode 1140. Stone Game II

    原题链接在这里:https://leetcode.com/problems/stone-game-ii/ 题目: Alex and Lee continue their games with pile ...

  8. BZOJ 1115: [POI2009]石子游戏Kam

    1115: [POI2009]石子游戏Kam Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 883  Solved: 545[Submit][Stat ...

  9. Games:取石子游戏(POJ 1067)

    取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37662   Accepted: 12594 Descripti ...

随机推荐

  1. css3 rem手机自适应框架

    css3 rem手机自适应框架 rem是按照html的字体大小来 所以 不同宽度浏览器 htmlfont-size不一样 就可以做到自适应了 此方法比百分比方便<pre><!DOCT ...

  2. 如何安装redis

    主要方式有四种:1.使用 Docker 安装.2.通过 Github 源码编译.3.直接安装 apt-get install(Ubuntu).yum install(RedHat) 或者 brew i ...

  3. 用Maven整合SSM框架

    前述 Maven 是专门用于构建和管理Java相关项目的工具,利用 Maven 的主要目的是统一维护 jar 包.关于 Maven 的安装在这篇里面就不说了. SSM(Spring+SpringMVC ...

  4. Spring Cloud 新一代Web框架微服务翘楚(一)

    序言 springcloud是微服务架构的集大成者,将一系列优秀的组件进行了整合.基于springboot构建,对我们熟悉spring的程序员来说,上手比较容易. 通过一些简单的注解,我们就可以快速的 ...

  5. 最全面的PS快捷键使用指南(图文演示)

    每次做图的时候都会记错快捷键,很苦恼有木有!!!只能各处搜寻PS快捷键汇总起来,老板再也不会说我作图慢了....... 1.Ctrl+T:自由变形 该快捷键,主要对图层进行旋转.缩放等变形调整,同时可 ...

  6. switch case加范围判断

    switch case是可以加范围判断的,但是语法有少许变化,参数不能写在switch里面,而是写在外面,如: const i = 3; switch (true) { case (i <= 0 ...

  7. mac os catalina mongodb最新安装流程

    1.brew安装 不推荐用brew,因为现在mongodb闭源了,brew里已经搜索不到mongodb,不过还是可以用brew安装的,这篇就不写了. 2.官网下载 直接去官网下载一个zip,解压完放到 ...

  8. 电信NBIOT 2 - 数据上行(中间件获取电信消息通知)

    电信NBIOT 1 - 数据上行(中国电信开发者平台对接流程) 电信NBIOT 2 - 数据上行(中间件获取电信消息通知) 电信NBIOT 3 - 数据下行 电信NBIOT 4 - NB73模块上行测 ...

  9. 图解Java数据结构之双向链表

    上一篇文章说到了单链表,也通过案例具体实现了一下,但是单链表的缺点也显而易见. 单向链表查找的方向只能是一个方向 单向链表不能自我删除,需要靠辅助节点 而双向链表则能够很轻松地实现上面的功能. 何为双 ...

  10. JVM 对象查询语言(OQL)[转载]

    最近生产环境出现一个很奇怪的问题,测试环境无法重现,本地直连生产无法重现.于是用上 jmap + Java VisualVM 的 OQL (Object Query Language) 分析问题. 关 ...