In the "100 game," two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.
What if we change the game so that players cannot re-use integers?
For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.
Given an integer maxChoosableInteger
and another integer desiredTotal
, determine if the first player to move can force a win, assuming both players play optimally.
You can always assume that maxChoosableInteger
will not be larger than 20 and desiredTotal
will not be larger than 300.
Example
Input: maxChoosableInteger = 10 desiredTotal = 11 Output: false Explanation: No matter which integer the first player choose, the first player will lose. The first player can choose an integer from 1 up to 10. If the first player choose 1, the second player can only choose integers from 2 up to 10. The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal. Same with other integers chosen by the first player, the second player will always win.
這道題給了我們一堆數字,然后兩個人,每人每次選一個數字,看數字總數誰先到給定值,有點像之前那道 Nim Game,但是比那題難度大。我剛開始想肯定說用遞歸啊,結果寫完發現 TLE 了,后來發現我們必須要優化效率,使用 HashMap 來記錄已經計算過的結果。我們首先來看如果給定的數字范圍大於等於目標值的話,直接返回 true。如果給定的數字總和小於目標值的話,說明誰也沒法贏,返回 false。然后我們進入遞歸函數,首先我們查找當前情況是否在 HashMap 中存在,有的話直接返回即可。我們使用一個整型數按位來記錄數組中的某個數字是否使用過,我們遍歷所有數字,將該數字對應的 mask 算出來,如果其和 used 相與為0的話,說明該數字沒有使用過,我們看如果此時的目標值小於等於當前數字,說明已經贏了,或者調用遞歸函數,如果返回 false,說明也是第一個人贏了。為啥呢,因為當前已經選過數字了,此時就該對第二個人調用遞歸函數,只有返回的結果是 false,我們才能贏,所以此時我們 true,並返回 true。如果遍歷完所有數字,標記 false,並返回 false,參見代碼如下:
class Solution { public: bool canIWin(int maxChoosableInteger, int desiredTotal) { if (maxChoosableInteger >= desiredTotal) return true; if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) return false; unordered_map<int, bool> m; return canWin(maxChoosableInteger, desiredTotal, 0, m); } bool canWin(int length, int total, int used, unordered_map<int, bool>& m) { if (m.count(used)) return m[used]; for (int i = 0; i < length; ++i) { int cur = (1 << i); if ((cur & used) == 0) { if (total <= i + 1 || !canWin(length, total - (i + 1), cur | used, m)) { m[used] = true; return true; } } } m[used] = false; return false; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/464
類似題目:
Guess Number Higher or Lower II
參考資料:
https://leetcode.com/problems/can-i-win/
https://leetcode.com/problems/can-i-win/discuss/95283/brute-force-and-memoization