题目
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.
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.
分析
玩取数字求和游戏,两人轮流选择一个数字,并记录已被选择数字累加和,谁可以先达到预定的数字就会赢,每个数字只能取一次。假设能取的最大值不超过20,目标值不超过300,两个人都会争取胜利。
主要思想是用动态规划,动态规划不仅仅包含前向和后向思想,也可以是用map保存以前计算过的结果。
因为两个人都会争取胜利,只要我能赢,那我肯定会选择赢。轮到另一个人选择的时候,如果所有可能的情况都不能赢(利用递归判断),那我就赢了。
这题用一个boolean
数组来记录数字被选择情况,每种选择情况可以对应一个结果,用递归来做时,会先计算依赖的子情况。而map<boolean[],Boolean>
是不合理的,因为数组作为key
的话是不能体现其内容的,考虑到最多只有20个数字可以选,可以将boolean[]
转换成Integer
,或用Arrays.toString()
代码
class Solution {
Map<Integer,Boolean> map; //保存每个可选状态下是否能赢
boolean[] used; //选择标记,只能选择未被选中的数字
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
int sum = (1 + maxChoosableInteger) * maxChoosableInteger / 2;
if(sum < desiredTotal) return false;
if(desiredTotal <= maxChoosableInteger) return true;
map = new HashMap();
used = new boolean[1 + maxChoosableInteger];
return helper(desiredTotal);
}
public boolean helper(int desiredTotal){
if(desiredTotal <= 0) return false; //上一轮达到了
int key = format(used); //目前的可选择情况,用Integer表示
if(map.containsKey(key)) return map.get(key); //之前已经计算过
//尝试每一个可选数字,有一种可能会赢就赢了,会递归到最小的范围,依次叠加
for(int i = 1; i < used.length; ++i){
if(!used[i]){
used[i] = true;
if(!helper(desiredTotal - i)){ //下一论选择不可能赢
map.put(key, true);
used[i] = false;
return true;
}
used[i] = false;
}
map.put(key, false);
}
return map.get(key);
}
public int format(boolean[] used){
int num = 0;
for(boolean b : used){
num <<= 1;
if(b) num |= 1;
}
return num;
}
}