Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins.
Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score.
Example 1:
Input: [1, 5, 2] Output: False Explanation: Initially, player 1 can choose between 1 and 2.
If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2).
So, final score of player 1 is 1 + 2 = 3, and player 2 is 5.
Hence, player 1 will never be the winner and you need to return False.
Example 2:
Input: [1, 5, 233, 7] Output: True Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233.
Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win.
Note:
- 1 <= length of the array <= 20.
- Any scores in the given array are non-negative integers and will not exceed 10,000,000.
- If the scores of both players are equal, then player 1 is still the winner.
這道題給了一個小游戲,有一個數組,兩個玩家輪流取數,說明了只能從開頭或結尾取,問我們第一個玩家能贏嗎。這道題博主想到了應該是用 Minimax 來做,由於之前有過一道這樣的題 Guess Number Higher or Lower II,所以依稀記得應該要用遞歸的方法,而且當前玩家贏返回 true 的條件就是遞歸調用下一個玩家輸返回 false。這里需要一個變量來標記當前是第幾個玩家,還需要兩個變量來分別記錄兩個玩家的當前數字和,在遞歸函數里面,如果當前數組為空了,直接比較兩個玩家的當前得分即可,如果數組中只有一個數字了,根據玩家標識來將這個數字加給某個玩家並進行比較總得分。如果數組有多個數字,分別生成兩個新數組,一個是去掉首元素,一個是去掉尾元素,然后根據玩家標識分別調用不同的遞歸,只要下一個玩家兩種情況中任意一種返回 false 了,那么當前玩家就可以贏了,參見代碼如下:
解法一:
class Solution { public: bool PredictTheWinner(vector<int>& nums) { return canWin(nums, 0, 0, 1); } bool canWin(vector<int> nums, int sum1, int sum2, int player) { if (nums.empty()) return sum1 >= sum2; if (nums.size() == 1) { if (player == 1) return sum1 + nums[0] >= sum2; else if (player == 2) return sum2 + nums[0] > sum1; } vector<int> va = vector<int>(nums.begin() + 1, nums.end()); vector<int> vb = vector<int>(nums.begin(), nums.end() - 1); if (player == 1) { return !canWin(va, sum1 + nums[0], sum2, 2) || !canWin(vb, sum1 + nums.back(), sum2, 2); } else if (player == 2) { return !canWin(va, sum1, sum2 + nums[0], 1) || !canWin(vb, sum1, sum2 + nums.back(), 1); } } };
我們還可以使用 DP 加 Minimax 的方法來做,先來看遞歸的寫法,十分的簡潔。DP 數組的作用是保存中間結果,再次遇到相同情況時直接返回不用再次計算,提高了運算效率:
解法二:
class Solution { public: bool PredictTheWinner(vector<int>& nums) { int n = nums.size(); vector<vector<int>> dp(n, vector<int>(n, -1)); return canWin(nums, 0, n - 1, dp) >= 0; } int canWin(vector<int>& nums, int s, int e, vector<vector<int>>& dp) { if (dp[s][e] == -1) { dp[s][e] = (s == e) ? nums[s] : max(nums[s] - canWin(nums, s + 1, e, dp), nums[e] - canWin(nums, s, e - 1, dp)); } return dp[s][e]; } };
下面這種方法是 DP 加 Minimax 的迭代寫法,要注意的是 DP 的更新順序,跟以往不太一樣,這種更新方法是按區間來更新的,感覺之前好像沒有遇到過這種更新的方法,還蠻特別的:
解法三:
class Solution { public: bool PredictTheWinner(vector<int>& nums) { int n = nums.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for (int i = 0; i < n; ++i) dp[i][i] = nums[i]; for (int len = 1; len < n; ++len) { for (int i = 0, j = len; j < n; ++i, ++j) { dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]); } } return dp[0][n - 1] >= 0; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/486
類似題目:
Guess Number Higher or Lower II
參考資料:
https://leetcode.com/problems/predict-the-winner/
https://leetcode.com/problems/predict-the-winner/discuss/96832/C%2B%2B-DP-solution-with-explanation