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 <= 300When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this problem involves exploring every single possible move, assuming both players play optimally. We recursively simulate every possible game state until we reach a terminal state (someone wins or the board is full). By exploring all these scenarios, we can determine if the first player can guarantee a win.
Here's how the algorithm would work step-by-step:
def can_i_win(max_integer, desired_total):
if desired_total <= 0:
return True
if (max_integer * (max_integer + 1)) // 2 < desired_total:
return False
def can_win(available_integers, current_total):
if current_total >= desired_total:
return False
# Check if this game state has already been computed.
state = tuple(available_integers)
if state in memo:
return memo[state]
# Iterate over remaining integers to check if any move guarantees a win
for integer in available_integers:
remaining_integers = available_integers.copy()
remaining_integers.remove(integer)
# Opponent tries to win, so negate the result
if not can_win(remaining_integers, current_total + integer):
memo[state] = True
return True
memo[state] = False
return False
memo = {}
available_integers = set(range(1, max_integer + 1))
return can_win(available_integers, 0)This problem asks whether the first player can always win a game where players take turns picking numbers from a pool, trying to reach a target sum. The key is to realize that winning depends on whether you can force your opponent into a losing position, which is best solved using a memorization technique.
Here's how the algorithm would work step-by-step:
def can_i_win(max_choosable_integer,
desired_total):
if desired_total <= 0:
return True
if (max_choosable_integer *
(max_choosable_integer + 1)) // 2 < desired_total:
return False
memo = {}
def can_win(available_numbers,
current_total):
tuple_representation = tuple(available_numbers)
if tuple_representation in memo:
return memo[tuple_representation]
# Check every possible number to pick
for number in available_numbers:
# If picking this number leads to immediate win
if current_total + number >= desired_total:
memo[tuple_representation] = True
return True
# If picking this number forces opponent to lose
remaining_numbers =
available_numbers - {number}
if not can_win(remaining_numbers,
current_total + number):
memo[tuple_representation] = True
return True
# If no winning move is found
memo[tuple_representation] = False
return False
all_numbers = set(range(1,
max_choosable_integer + 1))
# Begin the game
return can_win(all_numbers, 0)| Case | How to Handle |
|---|---|
| maxChoosableInteger is zero or negative | Return false since no positive integer can be chosen. |
| desiredTotal is zero or negative | Return true immediately since the first player wins by default. |
| Sum of all numbers is less than desiredTotal | Return false since it is impossible for either player to reach the desiredTotal. |
| maxChoosableInteger is greater than or equal to desiredTotal | Return true, as the first player can win by picking desiredTotal or higher in one turn. |
| desiredTotal is very large leading to excessive recursion/memoization | Consider the upper bound on desiredTotal imposed by the prompt and ensure int data type is sufficient |
| maxChoosableInteger close to the recursion limit (e.g. 20) may cause stack overflow if not memoized | Utilize memoization (e.g., a HashMap or array) to store the results of subproblems. |
| All numbers are chosen and the desiredTotal is not reached | The base case for this condition should return false (no winner) since no more numbers are available. |
| Integer overflow with large maxChoosableInteger and desiredTotal | Ensure that calculations of the running sum and available numbers are done with appropriate data types to prevent overflows (e.g., using long if necessary, though typically integer will suffice within problem constraints). |