In the "100 game" two players take turns adding, to a running total, any integer from 1 to 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 from 1 to 15 without replacement until they reach a total >= 100.
Given two integers maxChoosableInteger and desiredTotal, return true if the first player to move can force a win, otherwise, return false. Assume both players play optimally.
Example 1:
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.
Example 2:
Input: maxChoosableInteger = 10, desiredTotal = 0 Output: true
Example 3:
Input: maxChoosableInteger = 10, desiredTotal = 1 Output: true
Constraints:
1 <= maxChoosableInteger <= 200 <= desiredTotal <= 300Problem Overview: Two players take turns picking numbers from 1..maxChoosableInteger without reuse. Each picked number adds to a running total. The player who first makes the total reach or exceed desiredTotal wins. The task is to determine if the first player can force a win assuming both players play optimally.
Approach 1: Dynamic Programming with Bitmask (O(n · 2^n) time, O(2^n) space)
Represent the game state using a bitmask where each bit indicates whether a number has already been chosen. A mask of size n uniquely defines which moves remain. From any state, iterate through numbers 1..n; if number i is unused, simulate picking it and check whether the opponent loses from the resulting state. Memoize results for each mask so the same state is never recomputed. The key insight: if any move forces the opponent into a losing state, the current state is winning. This transforms the exponential search tree into a manageable 2^n state DP using Bit Manipulation and Dynamic Programming.
Approach 2: Minimax with Memoization (O(n · 2^n) time, O(2^n) space)
This models the problem as a classic two-player optimal strategy game. Use a recursive minimax search where the current player tries to maximize their chance of winning while the opponent tries to minimize it. At each step, iterate through available numbers and check if choosing one either immediately reaches the target or leads to a state where the opponent cannot win. Cache results using memoization keyed by the bitmask of chosen numbers. Without caching the recursion would explore up to n! paths, but memoization collapses equivalent states to 2^n. This approach highlights the Game Theory reasoning behind the optimal play.
Approach 3: Recursive DP with State Pruning (O(n · 2^n) time, O(2^n) space)
Another formulation stores the remaining total instead of the running sum and recursively tries available numbers. Before recursion, perform quick pruning checks. If the sum of all numbers 1..n is smaller than desiredTotal, winning is impossible. If any single number already reaches the target, the first player wins immediately. During recursion, subtract the chosen number from the remaining total and continue exploring unused numbers using a memo table indexed by the bitmask state. This is essentially top‑down DP with memoization but framed around remaining score rather than accumulated score.
Recommended for interviews: Use the minimax or bitmask DP approach with memoization. Interviewers expect you to recognize the game-state search and compress it using a bitmask. A naive recursive search demonstrates understanding of the game tree, but adding memoization and representing states with bit operations shows strong problem‑solving skill and reduces the complexity to O(n · 2^n).
This approach constructs a dynamic programming solution using bitmasking to represent the state of available numbers. We use a bottom-up strategy to determine if the player can force a winning move given the current pool of available numbers.
This Python solution uses a bottom-up dynamic programming approach with bitmasking. The array `dp` stores win/lose states for each possible combination of used integers, which is traversed and updated iteratively.
Python
Time Complexity: O(2^n) where n is the maxChoosableInteger. Space Complexity: O(2^n) due to the storage of states in the dp array.
This approach uses the Minimax algorithm with memoization to evaluate all possible game states, efficiently determining if the first player can guarantee a win. The idea is to explore all possible moves by the current player, then recursively simulate the opponent's optimal response. Memoization is employed to cache results of subproblems to avoid redundant calculations.
The function canIWin decides if a player can win given the constraints. It first checks for immediate game-ending scenarios. The recursive function can_win manages game state using an array to track used numbers, utilizing memoization to store and reuse results of previously computed states.
The time complexity is O(2^n) due to exploring all possible states, where n is maxChoosableInteger. The space complexity is also O(2^n) because of the memory used in the memoization map.
This approach uses dynamic programming to solve the problem. We create a table that keeps track of possible outcomes based on the current state of integers used. It optimizes the recursive solution by considering only valid states and storing their outcomes more directly.
This Python solution employs a dynamic programming strategy with memoization to track used numbers as binary masks. It efficiently avoids recomputation of previously evaluated states, thanks to the lru_cache mechanism.
Time complexity is O(2^n) similar to the minimax approach due to bitmasking possibilities, but this is managed efficiently via memoization. Space complexity is also O(2^n).
First, we check if the sum of all selectable integers is less than the target value. If so, it means that we cannot win no matter what, so we directly return false.
Then, we design a function dfs(mask, s), where mask represents the current state of the selected integers, and s represents the current cumulative sum. The return value of the function is whether the current player can win.
The execution process of the function dfs(mask, s) is as follows:
We iterate through each integer i from 1 to maxChoosableInteger. If i has not been selected, we can choose i. If the cumulative sum s + i after choosing i is greater than or equal to the target value desiredTotal, or if the result of the opponent choosing i is losing, then the current player is winning, return true.
If no choice can make the current player win, then the current player is losing, return false.
To avoid repeated calculations, we use a hash table f to record the calculated states, where the key is mask, and the value is whether the current player can win.
The time complexity is O(2^n), and the space complexity is O(2^n). Where n is maxChoosableInteger.
Python
Java
C++
Go
TypeScript
| Approach | Complexity |
|---|---|
| Dynamic Programming with Bitmask | Time Complexity: O(2^n) where n is the |
| Minimax with Memoization | The time complexity is O(2^n) due to exploring all possible states, where n is maxChoosableInteger. The space complexity is also O(2^n) because of the memory used in the memoization map. |
| Dynamic Programming | Time complexity is O(2^n) similar to the minimax approach due to bitmasking possibilities, but this is managed efficiently via memoization. Space complexity is also O(2^n). |
| State Compression + Memoization | — |
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Dynamic Programming with Bitmask | O(n · 2^n) | O(2^n) | Best general solution when n ≤ 20 and states can be represented as used-number masks |
| Minimax with Memoization | O(n · 2^n) | O(2^n) | When reasoning about optimal play in two-player games |
| Recursive DP with State Pruning | O(n · 2^n) | O(2^n) | Useful when framing the state as remaining target and applying early pruning checks |
花花酱 LeetCode 464. Can I Win - 刷题找工作 EP165 • Hua Hua • 10,434 views views
Watch 9 more video solutions →Practice Can I Win with our built-in code editor and test cases.
Practice on FleetCode