Skip to content

Commit 5a41329

Browse files
authored
feat: add solutions to lc problem: No.464 (#3055)
No.0464.Can I Win
1 parent ba1990e commit 5a41329

File tree

9 files changed

+261
-167
lines changed

9 files changed

+261
-167
lines changed

solution/0400-0499/0464.Can I Win/README.md

+89-54
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ tags:
7575

7676
### 方法一:状态压缩 + 记忆化搜索
7777

78+
我们首先判断可以选择的所有整数的和是否小于目标值,如果是,说明无论如何都无法赢,直接返回 `false`
79+
80+
然后,我们设计一个函数 $\text{dfs}(mask, s)$,其中 `mask` 表示当前已选择的整数的状态,`s` 表示当前的累计和。函数返回值为当前玩家是否能赢。
81+
82+
函数 $\text{dfs}(mask, s)$ 的执行过程如下:
83+
84+
我们遍历 $1$ 到 $maxChoosableInteger$ 中的每个整数 $i$,如果 $i$ 还没有被选择,我们可以选择 $i$,如果选择 $i$ 后的累计和 $s + i$ 大于等于目标值 `desiredTotal`,或者对手选择 $i$ 后的结果是输的,那么当前玩家就是赢的,返回 `true`
85+
86+
如果没有任何一个选择能让当前玩家赢,那么当前玩家就是输的,返回 `false`
87+
88+
为了避免重复计算,我们使用一个哈希表 `f` 记录已经计算过的状态,键为 `mask`,值为当前玩家是否能赢。
89+
90+
时间复杂度 $O(2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 是 `maxChoosableInteger`
91+
7892
<!-- tabs:start -->
7993

8094
#### Python3
@@ -83,16 +97,14 @@ tags:
8397
class Solution:
8498
def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:
8599
@cache
86-
def dfs(state, t):
100+
def dfs(mask: int, s: int) -> bool:
87101
for i in range(1, maxChoosableInteger + 1):
88-
if (state >> i) & 1:
89-
continue
90-
if t + i >= desiredTotal or not dfs(state | 1 << i, t + i):
91-
return True
102+
if mask >> i & 1 ^ 1:
103+
if s + i >= desiredTotal or not dfs(mask | 1 << i, s + i):
104+
return True
92105
return False
93106

94-
s = (1 + maxChoosableInteger) * maxChoosableInteger // 2
95-
if s < desiredTotal:
107+
if (1 + maxChoosableInteger) * maxChoosableInteger // 2 < desiredTotal:
96108
return False
97109
return dfs(0, 0)
98110
```
@@ -101,32 +113,33 @@ class Solution:
101113

102114
```java
103115
class Solution {
104-
private Map<Integer, Boolean> memo = new HashMap<>();
116+
private Map<Integer, Boolean> f = new HashMap<>();
117+
private int maxChoosableInteger;
118+
private int desiredTotal;
105119

106120
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
107-
int s = (1 + maxChoosableInteger) * maxChoosableInteger / 2;
108-
if (s < desiredTotal) {
121+
if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) {
109122
return false;
110123
}
111-
return dfs(0, 0, maxChoosableInteger, desiredTotal);
124+
this.maxChoosableInteger = maxChoosableInteger;
125+
this.desiredTotal = desiredTotal;
126+
return dfs(0, 0);
112127
}
113128

114-
private boolean dfs(int state, int t, int maxChoosableInteger, int desiredTotal) {
115-
if (memo.containsKey(state)) {
116-
return memo.get(state);
129+
private boolean dfs(int mask, int s) {
130+
if (f.containsKey(mask)) {
131+
return f.get(mask);
117132
}
118-
boolean res = false;
119-
for (int i = 1; i <= maxChoosableInteger; ++i) {
120-
if (((state >> i) & 1) == 0) {
121-
if (t + i >= desiredTotal
122-
|| !dfs(state | 1 << i, t + i, maxChoosableInteger, desiredTotal)) {
123-
res = true;
124-
break;
133+
for (int i = 0; i < maxChoosableInteger; ++i) {
134+
if ((mask >> i & 1) == 0) {
135+
if (s + i + 1 >= desiredTotal || !dfs(mask | 1 << i, s + i + 1)) {
136+
f.put(mask, true);
137+
return true;
125138
}
126139
}
127140
}
128-
memo.put(state, res);
129-
return res;
141+
f.put(mask, false);
142+
return false;
130143
}
131144
}
132145
```
@@ -137,24 +150,24 @@ class Solution {
137150
class Solution {
138151
public:
139152
bool canIWin(int maxChoosableInteger, int desiredTotal) {
140-
int s = (1 + maxChoosableInteger) * maxChoosableInteger / 2;
141-
if (s < desiredTotal) return false;
142-
unordered_map<int, bool> memo;
143-
return dfs(0, 0, maxChoosableInteger, desiredTotal, memo);
144-
}
145-
146-
bool dfs(int state, int t, int maxChoosableInteger, int desiredTotal, unordered_map<int, bool>& memo) {
147-
if (memo.count(state)) return memo[state];
148-
bool res = false;
149-
for (int i = 1; i <= maxChoosableInteger; ++i) {
150-
if ((state >> i) & 1) continue;
151-
if (t + i >= desiredTotal || !dfs(state | 1 << i, t + i, maxChoosableInteger, desiredTotal, memo)) {
152-
res = true;
153-
break;
154-
}
153+
if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) {
154+
return false;
155155
}
156-
memo[state] = res;
157-
return res;
156+
unordered_map<int, int> f;
157+
function<bool(int, int)> dfs = [&](int mask, int s) {
158+
if (f.contains(mask)) {
159+
return f[mask];
160+
}
161+
for (int i = 0; i < maxChoosableInteger; ++i) {
162+
if (mask >> i & 1 ^ 1) {
163+
if (s + i + 1 >= desiredTotal || !dfs(mask | 1 << i, s + i + 1)) {
164+
return f[mask] = true;
165+
}
166+
}
167+
}
168+
return f[mask] = false;
169+
};
170+
return dfs(0, 0);
158171
}
159172
};
160173
```
@@ -163,33 +176,55 @@ public:
163176
164177
```go
165178
func canIWin(maxChoosableInteger int, desiredTotal int) bool {
166-
s := (1 + maxChoosableInteger) * maxChoosableInteger / 2
167-
if s < desiredTotal {
179+
if (1+maxChoosableInteger)*maxChoosableInteger/2 < desiredTotal {
168180
return false
169181
}
170-
memo := map[int]bool{}
182+
f := map[int]bool{}
171183
var dfs func(int, int) bool
172-
dfs = func(state, t int) bool {
173-
if v, ok := memo[state]; ok {
184+
dfs = func(mask, s int) bool {
185+
if v, ok := f[mask]; ok {
174186
return v
175187
}
176-
res := false
177188
for i := 1; i <= maxChoosableInteger; i++ {
178-
if (state>>i)&1 == 1 {
179-
continue
180-
}
181-
if t+i >= desiredTotal || !dfs(state|1<<i, t+i) {
182-
res = true
183-
break
189+
if mask>>i&1 == 0 {
190+
if s+i >= desiredTotal || !dfs(mask|1<<i, s+i) {
191+
f[mask] = true
192+
return true
193+
}
184194
}
185195
}
186-
memo[state] = res
187-
return res
196+
f[mask] = false
197+
return false
188198
}
189199
return dfs(0, 0)
190200
}
191201
```
192202

203+
#### TypeScript
204+
205+
```ts
206+
function canIWin(maxChoosableInteger: number, desiredTotal: number): boolean {
207+
if (((1 + maxChoosableInteger) * maxChoosableInteger) / 2 < desiredTotal) {
208+
return false;
209+
}
210+
const f: Record<string, boolean> = {};
211+
const dfs = (mask: number, s: number): boolean => {
212+
if (f.hasOwnProperty(mask)) {
213+
return f[mask];
214+
}
215+
for (let i = 1; i <= maxChoosableInteger; ++i) {
216+
if (((mask >> i) & 1) ^ 1) {
217+
if (s + i >= desiredTotal || !dfs(mask ^ (1 << i), s + i)) {
218+
return (f[mask] = true);
219+
}
220+
}
221+
}
222+
return (f[mask] = false);
223+
};
224+
return dfs(0, 0);
225+
}
226+
```
227+
193228
<!-- tabs:end -->
194229

195230
<!-- solution:end -->

0 commit comments

Comments
 (0)