题目描述
leetcode 中等题
两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和 达到或超过 100 的玩家,即为胜者。
两位玩家不能使用重复的整数
给定两个整数 maxChoosableInteger (整数池中可选择的最大数)和 desiredTotal(累计和),若先出手的玩家是否能稳赢则返回 true ,否则返回 false 。假设两位玩家游戏时都表现 最佳 。
示例 1:
1 2 3 4 5 6 7 8
| 输入:maxChoosableInteger = 10, desiredTotal = 11 输出:false 解释: 无论第一个玩家选择哪个整数,他都会失败。 第一个玩家可以选择从 1 到 10 的整数。 如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。 第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利. 同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。
|
示例 2:
1 2
| 输入:maxChoosableInteger = 10, desiredTotal = 0 输出:true
|
示例 3:
1 2
| 输入:maxChoosableInteger = 10, desiredTotal = 1 输出:true
|
提示:
1 2
| 1 <= maxChoosableInteger <= 20 0 <= desiredTotal <= 300
|
递归 (TLE)
最无脑的做法是直接递归, 用一个 List
来模拟选数的情况,每次选走一个数时 remove
掉对应元素即可,但同时因为要找到最优决策,所以不能改变原集合,只能改变拷贝的 List
(如果直接对原集合 remove
,递归结束后不好还原,重新 add
会添加到集合末尾,那整个 for 循环遍历集合就不正确了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public boolean canIWin(int n, int dest) { return dfs(IntStream.rangeClosed(1, n).boxed().collect(Collectors.toList()), dest); }
private boolean dfs(List<Integer> list, int dest){ int n = list.size(); boolean ans = false; for(int i = 0; i < n; i++){ int item = list.get(i); if(item >= dest){ return true; } List<Integer> copy = new ArrayList<>(list); copy.remove(i); boolean flag = dfs(copy, dest - item); if(flag == false){ return true; } } return ans; } }
|
状态压缩 + 记忆化搜索
上面方案有问题的地方在于直接使用了 List
模拟选数过程,而整个递归过程是存在很多重复计算的,使用 List
无法进行记忆化搜索。
所以容易发现从一开始使用 List
就是一个 错误的思路 。
由于 n 数据范围为 20,且每个数只能被选择一次,所以我们可以 用一个 int (32位) 来表示选数的情况(状态压缩
),对应二进制为 1 代表已被选择,否则代表未被选择。这种方案的好处在于很容易就能实现 记忆化搜索
,不管是用 int[1 << 20]
又或者是 Map<Integer, Integer>
。
整体实现的逻辑还是和上面的版本一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { int[] cache = new int[1 << 20]; int n;
public boolean canIWin(int n, int dest) { if (n * (n + 1) / 2 < dest) return false; this.n = n; return dfs(0, dest) == 1; }
private int dfs(int state, int dest){ if(cache[state] != 0){ return cache[state]; } for(int i = 1; i <= n; i++){ if(((state >> (i - 1)) & 1) == 1){ continue; } if(i >= dest){ return cache[state] = 1; } if(dfs((state | (1 << (i - 1))), dest - i) == -1){ return cache[state] = 1; } } return cache[state] = -1; }
}
|