Leetcode 464.我能赢吗
我能赢吗
在 "100 game" 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到 100 的玩家,即为胜者。
如果我们将游戏规则改为 "玩家不能重复使用整数" 呢?
例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。
给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?
你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300。
示例:
输入:
maxChoosableInteger = 10
desiredTotal = 11
输出:
false
解释:
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。
DFS + memorizatoin
通过state保存状态,即当前选了哪些数
需要注意maxChoosableInteger不会大于20,我们完全可以通过一个32位的整数来表示状态
例如选择了1和2,那么状态为01 | 10 = 11 = 3(选择的数为1左移位数-1)
1 class Solution { 2 public boolean canIWin(int maxChoosableInteger, int desiredTotal) { 3 if(desiredTotal <= maxChoosableInteger) 4 return true; 5 //Note: n should be <= 32 as int is 32-bit in Java; else it will 1 << 33+ equals 0. 6 int n = maxChoosableInteger; 7 int sum = n * (n + 1) / 2; 8 if(sum < desiredTotal) 9 return false; 10 Boolean[] dp = new Boolean[1 << n]; 11 return canIWin(0, n, desiredTotal, dp); 12 } 13 14 private boolean canIWin(int state, int n, int remain, Boolean[] dp) { 15 if (remain <= 0) { 16 //dp[state] = false; 17 // Base case: 18 return false; 19 } 20 if (dp[state] == null) { 21 dp[state] = false; 22 int mask = 1; 23 //Key Point: take from the tail 24 for(int i = 1; i <= n; i++){ 25 int future = state | mask; 26 //the other can win 27 if (future != state && !canIWin(future, n, remain - i, dp)) { 28 //update current status = true 29 dp[state] = true; 30 break; 31 } 32 mask <<= 1; 33 } 34 } 35 return dp[state]; 36 } 37 }