题目地址:
https://leetcode.com/problems/can-i-win/
给定两个正整数 m m m和 d d d,两个人轮流玩游戏,每个人一开始得分都是 0 0 0,然后轮流在 1 ∼ m 1\sim m 1∼m里取数,同一个数字只能取一次。当所有被取出的数的总和第一次大于等于 d d d的时候,使得总和达到 d d d的那个人就获胜。问先手是否能保证胜利。
参考https://blog.csdn.net/qq_46105170/article/details/112550688,代码如下:
import java.util.Arrays;
public class Solution {
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) {
return false;
}
if (maxChoosableInteger >= desiredTotal) {
return true;
}
int[] f = new int[1 << maxChoosableInteger];
Arrays.fill(f, -1);
return dfs(0, 0, f, maxChoosableInteger, desiredTotal);
}
// state是当前走的人面对的局面,sum是当前数字总和,返回的是当前走的人是否能赢
private boolean dfs(int state, int sum, int[] f, int max, int target) {
// 有记忆则调取记忆
if (f[state] != -1) {
return f[state] == 1;
}
// 如果对方取数的时候总和达到target了,则当前走的人输了,做记忆并返回false
if (sum >= target) {
f[state] = 0;
return false;
}
// 枚举当前人取哪个数
for (int i = 1; i <= max; i++) {
if ((state >> i - 1 & 1) == 0 && !dfs(state | (1 << i - 1), sum + i, f, max, target)) {
f[state] = 1;
return true;
}
}
f[state] = 0;
return false;
}
}
时空复杂度 O ( 2 m ) O(2^m) O(2m)。