[LeetCode] 877. Stone Game 石子游戏



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].html

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

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.github

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 中的那样,用个 player 变量来记录当前是哪一个玩家在操做,若为0,表示 Alex 在选,那么他只有两种选择,要么拿首堆,要么拿尾堆,两种状况分别调用递归,两个递归函数只要有一个能返回 true,则表示 Alex 能够获胜,还须要用个变量 cur0 来记录当前 Alex 的石子总数。同理,若 Lee 在选,即 player 为1的时候,也是只有两种选择,分别调用递归,两个递归函数只要有一个能返回 true,则表示 Lee 能够获胜,用 cur1 来记录当前 Lee 的石子总数。须要注意的是,当首堆或尾堆被选走了后,咱们须要标记,这里就有两种方法,一种是从原 piles 中删除选走的堆(或者是新建一个不包含选走堆的数组),可是这种方法会包括大量的拷贝运算,没法经过 OJ。另外一种方法是用两个指针 left 和 right,分别指向首尾的位置。当选取了首堆时,则 left 自增1,若选了尾堆时,则 right 自减1。这样就不用执行删除操做,或是拷贝数组了,大大的提升了运行效率,参见代码以下:指针



解法一:code

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,博主详细的讲了这种 dp 的更新顺序,能够去看看,参见代码以下:htm



解法二:blog

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 有些像。缘由就在于题目中的一个条件,那就是总共有偶数堆,那么就是能够分为堆数相等的两堆,好比咱们按奇偶分为两堆。题目还说了石子总数为奇数个,那么分出的这两堆的石子总数必定是不相等的,那么咱们只要每次一直取石子总数多的奇数堆或者偶数堆,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 题目讲解汇总(持续更新中...)

相关文章
相关标签/搜索