207. Divisor Game
š LeetCode Problem: 1025. Divisor Game
š Difficulty: Easy
š·ļø Topics: Dynamic Programming, Math, Game Theory, Brainteaser, 1D DP
Problem Statement
Alice and Bob take turns playing a game, with Alice starting first.
Initially, there is a number n on the chalkboard. On each player's turn, that player makes a move consisting of:
- Choosing any
xwith0 < x < nandn % x == 0. - Replacing the number
non the chalkboard withn - x.
Also, if a player cannot make a move, they lose the game.
Return true if and only if Alice wins the game, assuming both players play optimally.
Example 1:
Input: n = 2
Output: true
Explanation:
Alice chooses 1, n becomes 1.
Bob cannot make a move, so Alice wins.
Example 2:
Input: n = 3
Output: false
Explanation:
Alice chooses 1, n becomes 2.
Bob chooses 1, n becomes 1.
Alice cannot make a move, so Bob wins.
Example 3:
Input: n = 4
Output: true
Explanation:
Alice chooses 1, n becomes 3.
Bob chooses 1, n becomes 2.
Alice chooses 1, n becomes 1.
Bob cannot make a move, so Alice wins.
Constraints:
- 1 <= n <= 1000
š³ Visual Understanding - The Game Theory Problem
The Problem We're Solving:
Two players: Alice (A) and Bob (B)
Turn order: Alice goes FIRST
Board: Number n on chalkboard
Each turn:
1. Choose x where: 0 < x < n AND n % x == 0 (x divides n)
2. Replace n with (n - x)
3. If cannot move ā LOSE!
Question: Does Alice win if both play optimally? š¤
Understanding Valid Moves:
For n = 6:
Divisors of 6: 1, 2, 3, 6
Valid choices for x: 1, 2, 3 (exclude 6 itself, must be < n)
Possible moves:
- Choose x=1 ā n becomes 6-1=5
- Choose x=2 ā n becomes 6-2=4
- Choose x=3 ā n becomes 6-3=3
For n = 5:
Divisors of 5: 1, 5
Valid choices for x: 1 (exclude 5 itself)
Possible moves:
- Choose x=1 ā n becomes 5-1=4
For n = 1:
Divisors of 1: 1
Valid choices for x: NONE (can't choose 1 because x < n, and 1 is not < 1)
Cannot move ā LOSE! ā
Game Tree for n = 4:
n=4 (Alice's turn)
ā
āāāāāāāāāāāāāā¼āāāāāāāāāāāāā
ā ā
x=1ā x=2ā
ā ā
n=3 n=2
(Bob's turn) (Bob's turn)
ā ā
āx=1 x=1ā
ā ā
n=2 n=1
(Alice's turn) (Alice LOSES!)
ā
x=1ā
ā
n=1
(Bob LOSES!)
Optimal play:
Alice chooses x=1 ā n=3
Bob chooses x=1 ā n=2
Alice chooses x=1 ā n=1
Bob cannot move ā Alice WINS! ā
Game Tree for n = 3:
n=3 (Alice's turn)
ā
āx=1 (only choice)
ā
n=2 (Bob's turn)
ā
āx=1 (only choice)
ā
n=1 (Alice LOSES!)
Optimal play:
Alice chooses x=1 ā n=2
Bob chooses x=1 ā n=1
Alice cannot move ā Bob WINS! ā
Pattern Observation:
Let's trace small values:
n=1: Alice cannot move ā Alice LOSES (false)
n=2: Alice chooses 1 ā n=1 ā Bob LOSES ā Alice WINS (true)
n=3: Alice chooses 1 ā n=2 ā Bob WINS ā Alice LOSES (false)
n=4: Alice chooses 1 ā n=3 ā Bob LOSES ā Alice WINS (true)
n=5: Alice chooses 1 ā n=4 ā Bob WINS ā Alice LOSES (false)
n=6: Alice can choose 1,2,3 ā best is choose 1 ā n=5 ā Bob LOSES ā Alice WINS (true)
Pattern:
n=1: false
n=2: true
n=3: false
n=4: true
n=5: false
n=6: true
OBSERVATION: Alice wins if n is EVEN! šÆ
Alice loses if n is ODD!
Why? We'll discover through DP! š
š§ The AHA Moment - Why Dynamic Programming?
Key Observation:
For current player at position n:
- They WIN if they can make a move that puts opponent in LOSING position
- They LOSE if ALL their moves put opponent in WINNING position
This is a classic GAME THEORY problem! š®
Recursive definition:
canWin(n) = true if EXISTS a move x such that:
- 0 < x < n
- n % x == 0
- canWin(n - x) = false (opponent loses!)
canWin(n) = false if ALL valid moves lead to:
- canWin(n - x) = true (opponent wins!)
Why Not Greedy?
Greedy approach: "Choose the largest divisor"
For n = 6:
Largest divisor < 6 = 3
Choose 3 ā n becomes 3
Opponent at n=3... do they win?
We don't know without exploring ALL possibilities!
Greedy doesn't work because:
- The "best" move depends on what happens LATER
- Need to think ahead: "If I do this, opponent does that..."
- Must explore ALL possible game paths
Need DP to determine optimal play! šÆ
Why Dynamic Programming?
1. OVERLAPPING SUBPROBLEMS:
To find canWin(6), we might check canWin(5), canWin(4), canWin(3)
To find canWin(4), we might check canWin(3), canWin(2)
Notice: canWin(3) needed MULTIPLE times! ā ļø
2. OPTIMAL SUBSTRUCTURE:
Whether current player wins depends on whether opponent wins
Solution to larger problem uses solutions to smaller ones
3. GAME THEORY:
Classic minimax pattern:
- I win if opponent loses
- I lose if opponent wins for ALL my moves
These properties = Perfect for DP! šÆ
š“ Approach 1: Brute Force (Recursion)
š Function Definition
Function Signature:
boolean divisorGame(int n)
What it represents:
Input Parameter n:
- The current number on the chalkboard
- Represents the game state/position
- Example: n=4 means "there's a 4 on the board"
Return Value (boolean):
- true = Current player (the one whose turn it is at position n) WINS
- false = Current player (the one whose turn it is at position n) LOSES
- Example: divisorGame(4) = true means "player starting at n=4 wins"
Purpose: - Determine if the player whose turn it is at position n will win - Assumes both players play optimally - Explores all possible moves and their outcomes
Key Understanding:
divisorGame(5) asks: "If it's my turn and n=5, do I win?"
Answer process:
- Try all valid moves (divisors of 5)
- For each move, check if opponent loses
- If ANY move makes opponent lose ā I win (return true)
- If ALL moves make opponent win ā I lose (return false)
Example:
divisorGame(2) = true
Meaning: "Player at position 2 wins"
Why: Choose x=1 ā opponent at position 1 ā opponent loses
š” Intuition
At position n, try ALL valid moves:
- For each divisor x of n (where 0 < x < n):
- Make move: n becomes (n - x)
- Check if opponent LOSES from position (n - x)
- If opponent loses ā we WIN!
If ANY move makes opponent lose ā we WIN
If ALL moves make opponent win ā we LOSE
Base case:
- n = 1: Cannot make any move ā LOSE (return false)
Recursion explores all game possibilities!
š Implementation
class Solution {
public boolean divisorGame(int n) {
// Base case: n = 1, cannot make a move
if (n == 1) {
return false; // Current player loses
}
// Try all possible moves
for (int x = 1; x < n; x++) {
// Check if x is a valid divisor
if (n % x == 0) {
// Make move: n becomes (n - x)
// If opponent LOSES from (n - x), we WIN!
if (!divisorGame(n - x)) {
return true; // Found a winning move!
}
}
}
// All moves lead to opponent winning
return false; // We lose
}
}
š Dry Run Example: n = 4
divisorGame(4) - "Does player at position 4 win?"
āā Try x=1: 4%1=0 ā valid
ā āā Check: !divisorGame(3) - "Does opponent lose at position 3?"
ā ā ā
ā ā āā divisorGame(3) - "Does player at position 3 win?"
ā ā ā āā Try x=1: 3%1=0 ā valid
ā ā ā ā āā Check: !divisorGame(2) - "Does opponent lose at position 2?"
ā ā ā ā ā ā
ā ā ā ā ā āā divisorGame(2) - "Does player at position 2 win?"
ā ā ā ā ā ā āā Try x=1: 2%1=0 ā valid
ā ā ā ā ā ā ā āā Check: !divisorGame(1)
ā ā ā ā ā ā ā ā āā divisorGame(1) ā return false (base case: cannot move)
ā ā ā ā ā ā ā āā !false = true
ā ā ā ā ā ā ā āā return true (found winning move!)
ā ā ā ā ā ā ā
ā ā ā ā ā ā āā divisorGame(2) = true ā "Player at 2 wins"
ā ā ā ā ā ā
ā ā ā ā ā āā !true = false
ā ā ā ā ā āā Continue checking other moves
ā ā ā ā ā
ā ā ā ā āā Try x=2: 3%2=1 ā invalid
ā ā ā ā āā No more valid moves
ā ā ā ā
ā ā ā āā return false (all moves lost)
ā ā ā Meaning: "Player at position 3 loses"
ā ā ā
ā ā āā divisorGame(3) = false ā "Player at 3 loses"
ā ā āā !false = true ā "Opponent loses!"
ā ā āā return true (found winning move!)
ā ā
ā āā !divisorGame(3) = !false = true
ā āā return true immediately!
ā
āā divisorGame(4) = true ā
Meaning: "Player at position 4 WINS"
Result: Alice starts at position 4 ā Alice WINS! ā
Function calls and their meanings:
divisorGame(4) = true ā "Player at 4 wins"
divisorGame(3) = false ā "Player at 3 loses"
divisorGame(2) = true ā "Player at 2 wins"
divisorGame(1) = false ā "Player at 1 loses" (base case)
š Complexity Analysis
Time Complexity: O(n!)
Worse than exponential!
At each level:
- Try up to (n-1) moves
- Each move branches to another recursive call
For n=4: ~24 calls
For n=10: ~3,628,800 calls! š„
Factorial explosion!
Space Complexity: O(n)
Recursion stack depth ā n
In worst case, go from n ā n-1 ā n-2 ā ... ā 1
Why This Fails:
ā Factorial time - extremely slow
ā Recalculates same subproblems many times
ā Too slow for n=1000
ā
But shows the game theory structure clearly!
š” Approach 2: Top-Down (Memoization)
š Function Definition
Function Signature:
boolean canWin(int n, Boolean[] memo)
What it represents:
Input Parameter n:
- The current number on the chalkboard
- Represents the game state/position
- Example: n=5 means "board shows 5"
Input Parameter memo:
- Memoization cache (array of size n+1)
- memo[i] stores the result for position i
- null = not yet calculated
- true = player at position i wins
- false = player at position i loses
Return Value (boolean):
- true = Current player at position n WINS with optimal play
- false = Current player at position n LOSES with optimal play
Purpose:
- Same as brute force BUT with caching
- Avoid recalculating same positions
- Store results in memo array for reuse
Key Understanding:
canWin(5, memo) asks: "If it's my turn at n=5, do I win?"
First time calling canWin(5, memo):
- memo[5] = null (not calculated)
- Calculate the answer
- Store in memo[5]
- Return the result
Second time calling canWin(5, memo):
- memo[5] already has answer
- Return memo[5] immediately (no recalculation!)
This is the OPTIMIZATION: cache hit = instant answer! ā”
š” Intuition - The Clever Conversion
PROBLEM IDENTIFIED in Brute Force:
We keep recalculating divisorGame(3), divisorGame(2), etc.
SOLUTION:
Cache (memoize) results the FIRST time we calculate them
Next time we need same value ā just look it up! šÆ
HOW TO IDENTIFY WHAT TO MEMOIZE:
1. Look at function parameters ā canWin(n, memo)
2. Parameter 'n' changes with each recursive call
3. For same 'n', result is always the same (deterministic game)
4. So memoize using 'n' as the key! š
5. Key range: 1 to n
CONVERSION FROM BRUTE FORCE:
1. Add a cache (array): memo[n]
- memo[n] = true ā player at position n wins
- memo[n] = false ā player at position n loses
2. Use special value to mark "not calculated yet"
- Can't use true/false (both valid answers)
- Use Boolean[] (null = not calculated) āØ
3. Before recursing, check cache
4. Store result in cache before returning
5. Same logic, just add caching!
šÆ Base Cases Transformation
BRUTE FORCE BASE CASES:
if (n == 1) return false; // Cannot move ā lose
TOP-DOWN BASE CASES (SAME!):
if (n == 1) return false; // Cannot move ā lose
Why same?
- Base case defines the game-ending condition
- "When n=1, current player cannot move and loses"
- Memoization doesn't change the rules, only adds caching
ONLY ADDITIONS:
1. Create memo array of Boolean[] (not boolean[])
- Boolean can be null (not calculated)
- boolean can only be true/false
2. Initialize all to null
3. Check cache before calculating
4. Store result in cache before returning
IMPORTANT - Why Boolean[] not boolean[]:
memo[i] states:
- null: not calculated yet
- true: player at position i wins
- false: player at position i loses
With boolean[], default is false
Can't distinguish "not calculated" from "loses"! ā ļø
š Implementation
class Solution {
public boolean divisorGame(int n) {
// Create memoization cache
// memo[i] = does player starting at position i win?
// Use Boolean[] (not boolean[]) to distinguish null (not calculated)
Boolean[] memo = new Boolean[n + 1];
return canWin(n, memo);
}
private boolean canWin(int n, Boolean[] memo) {
// Base case: n = 1, cannot make a move (SAME as brute force)
if (n == 1) {
return false; // Current player loses
}
// ⨠NEW: Check if already calculated
if (memo[n] != null) {
return memo[n]; // Cache HIT! Return stored result
}
// Calculate (same game logic as brute force)
// Try all possible moves
for (int x = 1; x < n; x++) {
// Check if x is a valid divisor
if (n % x == 0) {
// Make move: n becomes (n - x)
// If opponent LOSES from (n - x), we WIN!
if (!canWin(n - x, memo)) {
// ⨠NEW: Store in cache before returning
memo[n] = true;
return true; // Found winning move!
}
}
}
// All moves lead to opponent winning
// ⨠NEW: Store in cache before returning
memo[n] = false;
return false; // We lose
}
}
š Detailed Dry Run: n = 5
Input: n = 5
Goal: Does Alice win?
Initial State:
memo = [null, null, null, null, null, null] (size n+1 = 6)
ā ā ā ā ā ā
0 1 2 3 4 5
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Call: canWin(5, memo) - "Does player at position 5 win?"
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Step 1: canWin(5, memo)
n=5, not base case
memo[5] = null ā CACHE MISS
Try all divisors of 5:
- Divisors: 1, 5
- Valid x (0 < x < 5): only x=1
Try x=1:
5 % 1 = 0 ā valid
Check: !canWin(4, memo)
Recurse ā canWin(4, memo)
Step 2: canWin(4, memo) - "Does player at position 4 win?"
n=4, not base case
memo[4] = null ā CACHE MISS
Try all divisors of 4:
- Divisors: 1, 2, 4
- Valid x (0 < x < 4): x=1, x=2
Try x=1:
4 % 1 = 0 ā valid
Check: !canWin(3, memo)
Recurse ā canWin(3, memo)
Step 3: canWin(3, memo) - "Does player at position 3 win?"
n=3, not base case
memo[3] = null ā CACHE MISS
Try all divisors of 3:
- Divisors: 1, 3
- Valid x (0 < x < 3): only x=1
Try x=1:
3 % 1 = 0 ā valid
Check: !canWin(2, memo)
Recurse ā canWin(2, memo)
Step 4: canWin(2, memo) - "Does player at position 2 win?"
n=2, not base case
memo[2] = null ā CACHE MISS
Try all divisors of 2:
- Divisors: 1, 2
- Valid x (0 < x < 2): only x=1
Try x=1:
2 % 1 = 0 ā valid
Check: !canWin(1, memo)
Recurse ā canWin(1, memo)
Step 5: canWin(1, memo) - "Does player at position 1 win?"
n=1 ā BASE CASE
Return false (cannot move, lose)
Back to Step 4
Step 6: Back at canWin(2, memo)
Got canWin(1, memo) = false ā "Opponent loses!"
!false = true
Found winning move! x=1 works
Store: memo[2] = true ā
Return true
memo = [null, null, true, null, null, null]
ā
stored!
Meaning: memo[2] = true means "Player at position 2 wins"
Back to Step 3
Step 7: Back at canWin(3, memo)
Got canWin(2, memo) = true ā "Opponent wins!"
!true = false
x=1 doesn't work (opponent wins)
No more valid x to try
All moves failed
Store: memo[3] = false ā
Return false
memo = [null, null, true, false, null, null]
ā
stored!
Meaning: memo[3] = false means "Player at position 3 loses"
Back to Step 2
Step 8: Back at canWin(4, memo)
Got canWin(3, memo) = false ā "Opponent loses!"
!false = true
Found winning move! x=1 works
Store: memo[4] = true ā
Return true
memo = [null, null, true, false, true, null]
ā
stored!
Meaning: memo[4] = true means "Player at position 4 wins"
Back to Step 1
Step 9: Back at canWin(5, memo)
Got canWin(4, memo) = true ā "Opponent wins!"
!true = false
x=1 doesn't work (opponent wins)
No more valid x to try
All moves failed
Store: memo[5] = false ā
Return false
Final memo = [null, null, true, false, true, false]
ā
stored!
Meaning: memo[5] = false means "Player at position 5 loses"
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
RESULT: false (Alice LOSES) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Understanding memo values:
memo[2] = true ā "Player at position 2 wins"
memo[3] = false ā "Player at position 3 loses"
memo[4] = true ā "Player at position 4 wins"
memo[5] = false ā "Player at position 5 loses"
Pattern in memo:
EVEN positions ā true (win)
ODD positions ā false (lose)
Pattern discovered: EVEN wins, ODD loses! šÆ
š Complexity Analysis
Time Complexity: O(n²)
Each position 1 to n calculated EXACTLY ONCE: O(n)
For each position, try all divisors: O(n) in worst case
Total: O(n) à O(n) = O(n²)
Actually, trying divisors is faster:
- Average number of divisors of n ā O(ān)
- So realistic time ā O(nān)
Still, upper bound is O(n²) ā
Massive improvement from O(n!)! š
Space Complexity: O(n)
Memoization array: O(n)
Recursion stack depth: O(n) worst case
Total: O(n) + O(n) = O(n)
š¢ Approach 3: Bottom-Up (Tabulation)
š Function Definition
DP Array Definition:
boolean[] dp = new boolean[n + 1];
What dp[i] represents:
Index i:
- Position/state in the game
- The number currently on the chalkboard
- Example: i=4 means "board shows 4"
Value dp[i] (boolean):
- dp[i] = true ā Player whose turn it is at position i WINS
- dp[i] = false ā Player whose turn it is at position i LOSES
- Example: dp[4] = true means "player at position 4 wins"
Purpose: - Store the answer for each position from 1 to n - Build solutions from smaller positions to larger ones - Bottom-up approach: start from base case, build to n
Key Understanding:
dp[3] = false means:
"If it's my turn and the board shows 3, I will LOSE"
Why?
- Only valid move: choose x=1 ā opponent gets 2
- dp[2] = true ā opponent wins
- All my moves lead to opponent winning ā I lose
dp[4] = true means:
"If it's my turn and the board shows 4, I will WIN"
Why?
- Valid moves: x=1 or x=2
- Choose x=1 ā opponent gets 3
- dp[3] = false ā opponent loses
- Found a move where opponent loses ā I win!
Building dp table = answering "do I win?" for each position
š” Intuition - The Clever Conversion from Top-Down
TOP-DOWN (Recursion + Memoization):
Start from n ā recurse down to smaller values ā build back
canWin(5, memo)
ā needs
canWin(4, memo)
ā needs
canWin(3, memo)
ā needs
canWin(2, memo)
ā needs
canWin(1, memo) = false (base case)
Direction: TOP ā DOWN to base, then back UP
BOTTOM-UP (Iteration + Tabulation):
Start from base case ā build up to n iteratively
dp[1] = false (base case)
ā build
dp[2] = can I make move to reach dp[j] where dp[j]=false?
ā build
dp[3] = can I make move to reach dp[j] where dp[j]=false?
ā build
...
ā build
dp[n]
Direction: BOTTOM (base) ā UP to n
KEY INSIGHT:
For position i, check ALL valid moves:
- For each divisor x of i (where 0 < x < i):
- New position = i - x
- If dp[i-x] = false (opponent loses) ā dp[i] = true!
If NO move makes opponent lose ā dp[i] = false
šÆ Base Cases Transformation - VERY IMPORTANT! š
TOP-DOWN BASE CASES (in recursion):
if (n == 1) return false; // Cannot move ā lose
This is a TERMINATION condition:
"When we hit n=1 during recursion, return false immediately"
BOTTOM-UP BASE CASES (in DP table):
dp[1] = false; // Cannot move ā lose
This is an INITIALIZATION value:
"Start with dp[1]=false, build from here"
KEY INSIGHT - THE CLEVER THINKING:
1. In top-down: "What do I return when I can't recurse further?"
ā n=1 is the smallest case ā return false
2. In bottom-up: "What is the smallest subproblem I know?"
ā dp[1] is the smallest case ā initialize to false
3. The VALUE is the same (false), but the ROLE is different:
- Top-down: BASE = where recursion STOPS
- Bottom-up: BASE = where iteration STARTS
EXAMPLE:
Top-down: if (n == 1) return false;
ā
Bottom-up: dp[1] = false;
Same value (false), different purpose! šÆ
HOW TO CONVERT:
Step 1: Identify base case from top-down ā dp[1] = false
Step 2: Start iteration from i=2 (next position after base)
Step 3: For each i, check all valid moves
Step 4: If ANY move leads to dp[j]=false ā dp[i]=true
Step 5: Otherwise dp[i]=false
š Recurrence Relation
Definition:
dp[i] = does player starting at position i win?
For position i, try all valid moves:
- For each x where (0 < x < i) AND (i % x == 0):
- New position = i - x
- If dp[i-x] = false ā opponent loses ā dp[i] = true!
If NO such move exists:
dp[i] = false (all moves lead to opponent winning)
Formula:
dp[i] = true if EXISTS x: (0<x<i) AND (i%x==0) AND dp[i-x]=false
dp[i] = false otherwise
Dependencies:
dp[i] depends on dp[i-1], dp[i-2], ..., dp[1]
(all smaller positions reachable in one move)
Must calculate in order: 1, 2, 3, ..., n
Can't skip ahead! Must build sequentially! šÆ
š Implementation
class Solution {
public boolean divisorGame(int n) {
// Create DP table
// dp[i] = does player starting at position i win?
boolean[] dp = new boolean[n + 1];
// Initialize base case (VERY IMPORTANT!)
// At position 1, cannot make a move ā lose
dp[1] = false;
// Build up from base case to n
// Start from 2 because we already know dp[1]
for (int i = 2; i <= n; i++) {
// Initially assume we lose
dp[i] = false;
// Try all possible moves
for (int x = 1; x < i; x++) {
// Check if x is a valid divisor
if (i % x == 0) {
// Make move: i becomes (i - x)
// If opponent LOSES from (i - x), we WIN!
if (!dp[i - x]) {
dp[i] = true; // Found winning move!
break; // No need to check other moves
}
}
}
}
// Answer is at dp[n]
return dp[n];
}
}
š Detailed Dry Run: n = 6
Input: n = 6
Goal: Does player starting at position 6 win?
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
INITIALIZATION
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Create DP table:
dp = [false, false, false, false, false, false, false]
ā ā ā ā ā ā ā
0 1 2 3 4 5 6
Initialize base case:
dp[1] = false (cannot move from position 1)
dp = [false, false, false, false, false, false, false]
ā
base case ā
Meaning: dp[1] = false ā "Player at position 1 loses"
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ITERATION - Building up to n
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Iteration 1: i = 2
āāāāāāāāāāāāāāāā
Question: "Does player at position 2 win?"
Position 2, try all divisors:
Divisors of 2: 1, 2
Valid x (0 < x < 2): only x=1
Try x=1:
2 % 1 = 0 ā valid
New position: 2-1 = 1
Check dp[1] = false ā "Opponent loses!"
We win!
dp[2] = true
dp = [false, false, true, false, false, false, false]
ā
computed!
Meaning: dp[2] = true ā "Player at position 2 wins"
Strategy: Choose x=1 ā opponent at position 1 ā opponent loses
Iteration 2: i = 3
āāāāāāāāāāāāāāāā
Question: "Does player at position 3 win?"
Position 3, try all divisors:
Divisors of 3: 1, 3
Valid x (0 < x < 3): only x=1
Try x=1:
3 % 1 = 0 ā valid
New position: 3-1 = 2
Check dp[2] = true ā "Opponent wins!"
Bad move!
No winning move found
dp[3] = false
dp = [false, false, true, false, false, false, false]
ā
computed!
Meaning: dp[3] = false ā "Player at position 3 loses"
All moves lead to opponent winning
Iteration 3: i = 4
āāāāāāāāāāāāāāāā
Question: "Does player at position 4 win?"
Position 4, try all divisors:
Divisors of 4: 1, 2, 4
Valid x (0 < x < 4): x=1, x=2
Try x=1:
4 % 1 = 0 ā valid
New position: 4-1 = 3
Check dp[3] = false ā "Opponent loses!"
We win!
dp[4] = true (stop checking)
dp = [false, false, true, false, true, false, false]
ā
computed!
Meaning: dp[4] = true ā "Player at position 4 wins"
Strategy: Choose x=1 ā opponent at position 3 ā opponent loses
Iteration 4: i = 5
āāāāāāāāāāāāāāāā
Question: "Does player at position 5 win?"
Position 5, try all divisors:
Divisors of 5: 1, 5
Valid x (0 < x < 5): only x=1
Try x=1:
5 % 1 = 0 ā valid
New position: 5-1 = 4
Check dp[4] = true ā "Opponent wins!"
Bad move!
No winning move found
dp[5] = false
dp = [false, false, true, false, true, false, false]
ā
computed!
Meaning: dp[5] = false ā "Player at position 5 loses"
All moves lead to opponent winning
Iteration 5: i = 6 (FINAL)
āāāāāāāāāāāāāāāāāāāāāāāāā
Question: "Does player at position 6 win?"
Position 6, try all divisors:
Divisors of 6: 1, 2, 3, 6
Valid x (0 < x < 6): x=1, x=2, x=3
Try x=1:
6 % 1 = 0 ā valid
New position: 6-1 = 5
Check dp[5] = false ā "Opponent loses!"
We win!
dp[6] = true (stop checking)
Final DP table:
dp = [false, false, true, false, true, false, true]
ā
ANSWER!
Meaning: dp[6] = true ā "Player at position 6 wins"
Strategy: Choose x=1 ā opponent at position 5 ā opponent loses
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
COMPLETE DP TABLE VISUALIZATION
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Position: 0 1 2 3 4 5 6
āāāāāāā¬āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
dp[] āfalseāfalse ātrue āfalseātrue āfalseātrue ā
āāāāāāā“āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
ā ā ā ā ā ā
base WIN LOSE WIN LOSE WIN
Understanding each value:
dp[1] = false ā "At position 1, player loses"
dp[2] = true ā "At position 2, player wins"
dp[3] = false ā "At position 3, player loses"
dp[4] = true ā "At position 4, player wins"
dp[5] = false ā "At position 5, player loses"
dp[6] = true ā "At position 6, player wins"
Pattern Discovered:
EVEN positions (2,4,6) ā true (WIN)
ODD positions (1,3,5) ā false (LOSE)
PATTERN: EVEN positions WIN, ODD positions LOSE! šÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
RESULT: true (Alice starts at position 6 ā Alice WINS) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š State Transition Diagram
dp[i] depends on ALL smaller positions reachable in one move:
For position 6:
dp[1] dp[2] dp[3] dp[4] dp[5]
ā ā ā ā ā
(6-5) (6-4) (6-3) (6-2) (6-1)
ā ā ā ā ā
āāāāāāāā“āāāāāāā“āāāāāāā“āāāāāāā
ā
dp[6]
Only divisors of 6 create valid edges:
x=1: 6-1=5 ā dp[5]=false ā (winning move!)
x=2: 6-2=4 ā dp[4]=true
x=3: 6-3=3 ā dp[3]=false ā (also winning!)
If ANY reachable position has dp[j]=false ā dp[i]=true
Build sequentially from 1ān to ensure dependencies ready! šÆ
š Complexity Analysis
Time Complexity: O(n²)
Loop from 2 to n: O(n)
For each i, try divisors: O(n) worst case
Total: O(n) à O(n) = O(n²)
Actually faster in practice:
Average divisors per number ā O(ān)
Realistic: O(nān)
Upper bound: O(n²) ā
Space Complexity: O(n)
DP table of size (n+1): O(n)
No recursion stack: O(1)
Total: O(n)
šµ Approach 4: Space-Optimized (Mathematical Pattern)
š Function Definition
Function Signature:
boolean divisorGame(int n)
What it represents:
Input Parameter n:
- The initial number on the chalkboard
- Starting position of the game
- Example: n=100 means "game starts with 100 on board"
Return Value (boolean):
- true = Alice (first player) WINS with optimal play
- false = Alice (first player) LOSES with optimal play
- Direct answer using mathematical pattern
Purpose: - Instantly determine winner using parity (even/odd) - No recursion, no DP table needed - O(1) solution based on mathematical proof
Key Understanding:
divisorGame(100) asks: "Does Alice win starting at 100?"
Pattern-based answer:
100 is EVEN ā return true
Why?
Mathematical proof shows:
EVEN ā first player wins
ODD ā first player loses
No need to simulate the game!
Just check: n % 2 == 0
This is the ULTIMATE optimization:
From O(n²) DP to O(1) math! š
š” Intuition - Discovering the Pattern
From our DP table, we noticed:
dp[1] = false (odd)
dp[2] = true (even)
dp[3] = false (odd)
dp[4] = true (even)
dp[5] = false (odd)
dp[6] = true (even)
PATTERN: n is EVEN ā true, n is ODD ā false
Can we prove this mathematically? š¤
MATHEMATICAL PROOF:
āāāāāāāāāāāāāāāāāā
1. Base case: n=1 (odd) ā false ā
2. For EVEN n:
- All divisors of even number include 1
- Choose x=1 ā new position = n-1 (odd)
- Opponent at odd position loses
- So even position wins! ā
3. For ODD n:
- All divisors of odd are also odd
- Choose any odd x ā new position = n-x (odd-odd = even)
- Opponent at even position wins
- So odd position loses! ā
This proves: EVEN wins, ODD loses! šÆ
So we don't need DP at all - just check if n is even!
š Implementation
class Solution {
public boolean divisorGame(int n) {
// Mathematical solution: Alice wins if n is even
return n % 2 == 0;
}
}
š Detailed Dry Run: n = 100
Input: n = 100
Question: "Does Alice win starting at position 100?"
Check: Is 100 even?
100 % 2 = 0
Yes, 100 is even!
Return: true
Result: Alice WINS! ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Why this works - Game simulation:
n = 100 (EVEN, Alice's turn)
Alice's strategy:
- Always choose x=1 (always valid divisor)
- 100 - 1 = 99 (ODD, Bob's turn)
Bob at 99 (ODD):
- Bob must choose odd divisor (1 is only option)
- 99 - 1 = 98 (EVEN, Alice's turn)
Alice at 98 (EVEN):
- Choose x=1
- 98 - 1 = 97 (ODD, Bob's turn)
Pattern continues:
100 ā 99 ā 98 ā 97 ā ... ā 4 ā 3 ā 2 ā 1
Eventually:
Alice at 2 (EVEN, Alice's turn)
- Choose x=1
- 2 - 1 = 1 (Bob's turn)
Bob at 1 (ODD):
- Cannot move (no valid x)
- Bob LOSES!
Alice WINS! ā
Mathematical guarantee:
EVEN ā can always force opponent to ODD
ODD ā can only give opponent EVEN
Alice starts with EVEN ā she controls the game ā she wins! šÆ
šÆ Why This Pattern Works - Mathematical Proof
THEOREM: Alice wins if and only if n is EVEN
PROOF BY INDUCTION:
āāāāāāāāāāāāāāāāāā
Base cases:
n=1: odd ā Alice loses (cannot move) ā
n=2: even ā Alice wins (choose x=1, Bob at 1) ā
Inductive step:
Assume pattern holds for all k < n
Case 1: n is EVEN
- Divisors of EVEN include 1 (always)
- Alice chooses x=1 ā Bob at (n-1), which is ODD
- By induction hypothesis, player at ODD loses
- Bob loses ā Alice WINS! ā
Case 2: n is ODD
- All divisors of ODD are also ODD
- Alice chooses any odd divisor x
- New position = n-x = ODD-ODD = EVEN
- By induction hypothesis, player at EVEN wins
- All Alice's moves give Bob EVEN position
- Bob wins from all moves ā Alice LOSES! ā
QED: EVEN wins, ODD loses! šÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
WHY ODD - ODD = EVEN?
Any odd number can be written as: 2k + 1 (for some integer k)
n = 2a + 1 (n is odd)
x = 2b + 1 (x is odd divisor)
n - x = (2a + 1) - (2b + 1)
= 2a - 2b
= 2(a - b)
= EVEN! ā
This is why odd positions always lose:
Can only move to even positions! šÆ
š Complexity Analysis
Time Complexity: O(1) ā
Single modulo operation
Constant time!
Improvement:
O(n²) ā O(1) ššš
MASSIVE speedup!
Space Complexity: O(1) ā
No array, no recursion
Just one boolean return
Constant space!
Improvement:
O(n) ā O(1) š
šÆ Pattern Recognition - Game Theory DP
Core Pattern: Minimax Game Theory
Template for two-player game DP:
canWin(state) = true if EXISTS a move such that:
opponent LOSES from new state
canWin(state) = false if ALL moves lead to:
opponent WINS from new state
For Divisor Game:
canWin(n) = true if EXISTS x: (0<x<n) AND (n%x==0) AND !canWin(n-x)
canWin(n) = false otherwise
Similar Problems:
- Nim Game: canWinNim(n) = (n % 4 != 0)
- Stone Game: Optimal play with picking from ends
- Predict the Winner: Two players picking from array
How to Identify Game Theory DP:
ā
Two players taking turns
ā
Both play optimally
ā
Win/lose based on game rules
ā
Current state determines future possibilities
ā
Recursive game tree structure
For Divisor Game:
ā
Alice and Bob alternate
ā
Both play optimally
ā
Cannot move ā lose
ā
Position n determines valid moves
ā
Game tree from n ā n-x ā ...
Perfect for Game Theory DP! šÆ
BONUS: Sometimes has mathematical pattern!
Look for even/odd, modulo, or number theory patterns
ā ļø Important Edge Cases to Test
1. Minimum Constraint: n = 1
Input: n = 1
Expected: false
Why important:
- BASE CASE of the problem
- Cannot make any move (no x where 0 < x < 1)
- Tests base case handling
Function meaning:
divisorGame(1) asks: "Does player at position 1 win?"
Answer: false ā "Player at position 1 loses"
Test all approaches:
Brute Force: Base case returns false ā
Top-Down: memo[1] never set (base case) ā
Bottom-Up: dp[1] = false (initialization) ā
Pattern: 1 is ODD ā false ā
2. Minimum Winning Case: n = 2
Input: n = 2
Expected: true
Why important:
- Smallest n where Alice wins
- Only one valid move (x=1)
- Tests simple winning scenario
Function meaning:
divisorGame(2) asks: "Does player at position 2 win?"
Answer: true ā "Player at position 2 wins"
Why?
- Choose x=1 ā opponent at position 1
- divisorGame(1) = false ā opponent loses
- Found winning move ā return true
Test all approaches:
Brute Force: Finds x=1 leads to loss for opponent ā
Top-Down: memo[2] = true after calculation ā
Bottom-Up: dp[2] = true (dp[1]=false, found winning move) ā
Pattern: 2 is EVEN ā true ā
3. Small Odd Number: n = 3
Input: n = 3
Expected: false
Why important:
- Smallest odd losing case
- Tests odd number behavior
- Validates pattern
Function meaning:
divisorGame(3) asks: "Does player at position 3 win?"
Answer: false ā "Player at position 3 loses"
Why?
- Only valid move: x=1
- Choose x=1 ā opponent at position 2
- divisorGame(2) = true ā opponent wins
- All moves lead to opponent winning ā return false
Test all approaches:
Brute Force: All moves (just x=1) lead to opponent win ā
Top-Down: memo[3] = false after checking all moves ā
Bottom-Up: dp[3] = false (dp[2]=true, no winning move) ā
Pattern: 3 is ODD ā false ā
4. Number with Multiple Divisors: n = 12
Input: n = 12
Expected: true
Why important:
- Many divisors: 1, 2, 3, 4, 6
- Tests optimal choice among multiple moves
- Divisors include both even and odd
Function meaning:
divisorGame(12) asks: "Does player at position 12 win?"
Answer: true ā "Player at position 12 wins"
Why?
Valid divisors: 1, 2, 3, 4, 6
Possible new positions: 11, 10, 9, 8, 6
Check each:
x=1 ā position 11 (odd) ā divisorGame(11) = false ā
Found winning move!
Test all approaches:
Brute Force: Tries all divisors, finds x=1 works ā
Top-Down: memo[12] = true (finds winning move) ā
Bottom-Up: dp[12] = true (11 is losing position) ā
Pattern: 12 is EVEN ā true ā
5. Prime Number: n = 7
Input: n = 7
Expected: false
Why important:
- Prime has only one divisor: 1
- Limited move options
- Tests single-move scenarios
Function meaning:
divisorGame(7) asks: "Does player at position 7 win?"
Answer: false ā "Player at position 7 loses"
Why?
- 7 is prime ā only divisor is 1
- Choose x=1 ā opponent at position 6
- divisorGame(6) = true ā opponent wins
- All moves (just one) lead to opponent winning ā return false
Test all approaches:
Brute Force: Only x=1 available, leads to opponent win ā
Top-Down: memo[7] = false (no winning move found) ā
Bottom-Up: dp[7] = false (dp[6]=true, no winning move) ā
Pattern: 7 is ODD ā false ā
6. Large Even Number: n = 1000
Input: n = 1000
Expected: true
Why important:
- Maximum constraint boundary
- Tests performance/correctness at scale
- Large even number
Function meaning:
divisorGame(1000) asks: "Does Alice win starting at 1000?"
Answer: true ā "Alice wins"
Test all approaches:
Brute Force: O(1000!) - TOO SLOW ā
Top-Down: O(1000²) = 1,000,000 operations (acceptable) ā
Bottom-Up: O(1000²) = 1,000,000 operations (acceptable) ā
Pattern: 1000 is EVEN ā O(1) instant! āāā
7. Large Odd Number: n = 999
Input: n = 999
Expected: false
Why important:
- Near maximum constraint
- Large odd number
- Tests boundary behavior
Function meaning:
divisorGame(999) asks: "Does Alice win starting at 999?"
Answer: false ā "Alice loses"
Test all approaches:
Bottom-Up: Builds dp table to 999 ā
Pattern: 999 is ODD ā false ā
8. Power of 2: n = 16
Input: n = 16
Expected: true
Why important:
- Special structure (2^4)
- Divisors: 1, 2, 4, 8
- Tests special number properties
Function meaning:
divisorGame(16) asks: "Does player at position 16 win?"
Answer: true ā "Player wins"
Many divisors, many winning paths:
x=1 ā 15 (odd, opponent loses) ā
x=2 ā 14 (even, opponent wins)
x=4 ā 12 (even, opponent wins)
x=8 ā 8 (even, opponent wins)
At least one winning move exists!
Test all approaches:
All approaches work correctly ā
Pattern: 16 is EVEN ā true ā
9. Perfect Square: n = 9
Input: n = 9
Expected: false
Why important:
- Perfect square (3²)
- Divisors: 1, 3
- Odd perfect square
Function meaning:
divisorGame(9) asks: "Does player at position 9 win?"
Answer: false ā "Player loses"
Divisors: 1, 3 (both odd)
Possible positions: 8, 6 (both even)
All moves lead to even positions (opponent wins)
Test all approaches:
All approaches correctly identify no winning move ā
Pattern: 9 is ODD ā false ā
10. Consecutive Numbers: n = 4 and n = 5
Input: n = 4
Expected: true
Input: n = 5
Expected: false
Why important:
- Tests alternating pattern
- Consecutive even and odd
- Validates even/odd rule
Function meaning:
divisorGame(4) ā true: "Player at 4 wins"
divisorGame(5) ā false: "Player at 5 loses"
Shows the pattern flip between consecutive numbers
Test all approaches:
Both values correctly computed by all approaches ā
Pattern: 4 EVEN ā true, 5 ODD ā false ā
Summary - Edge Case Coverage
Category Test Cases Pattern Result
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Minimum (n=1) 1 false (odd)
Small numbers 2,3,4,5 alternating
Multiple divisors 12 true (even)
Prime numbers 7 false (odd)
Large numbers 999, 1000 odd/even
Powers of 2 16 true (even)
Perfect squares 9 false (odd)
Boundary 1, 1000 min/max
All cases validate: EVEN ā true, ODD ā false ā
Function definition clarity:
divisorGame(n) = "Does player at position n win?"
Return: true/false based on optimal play
Clear understanding prevents confusion! šÆ
š Quick Revision Notes
šÆ Core Concept
Problem: Two-player game. Players alternate choosing divisor x of n, then replacing n with n-x. Player who cannot move loses. Does Alice (first player) win?
Function Definition:
- divisorGame(n) or canWin(n) = Does player at position n win with optimal play?
- dp[i] = Does player at position i win with optimal play?
- Return: true (wins) or false (loses)
Key Insight: This is a game theory DP problem. Player wins if they can make a move that puts opponent in losing position. Formula: canWin(n) = true if EXISTS x: (0<x<n) AND (n%x==0) AND !canWin(n-x)
PATTERN DISCOVERY: Alice wins if and only if n is EVEN! šÆ
Four Approaches: 1. Brute Force: Recursion trying all moves ā O(n!) time ā 2. Top-Down: Add memoization ā O(n²) time, O(n) space ā 3. Bottom-Up: Iterative DP table ā O(n²) time, O(n) space āā 4. Mathematical: Check if even ā O(1) time, O(1) space āāā
ā” Quick Implementation
public class Solution {
public boolean divisorGame(int n) {
// // Approach 1 - recursion
// return recursive(n);
// // Approach 2 - top down
// Boolean[] memo = new Boolean[n + 1];
// return topDown(n, memo);
// Approach 3 - bottom up
return bottomUp(n);
// // Approach 4 - bottom up space optimized
// return bottomUpSO(n);
}
private boolean bottomUpSO(int n) {
return n % 2 == 0;
}
private boolean bottomUp(int n) {
Boolean[] dp = new Boolean[n + 1];
// base case
dp[1] = false;
for (int i = 2; i <= n; i++) {
dp[i] = false;
for (int x = 1; x < i; x++) {
if (i % x == 0) {
if (!dp[i - x]) {
dp[i] = true;
break;
}
}
}
}
return dp[n];
}
private boolean topDown(int n, Boolean[] memo) {
// same base case
if (n == 1) {
return false;
}
if (memo[n] != null) {
return memo[n];
}
for (int x = 1; x < n; x++) {
if (n % x == 0) {
if (!topDown(n - x, memo)) {
return memo[n] = true;
}
}
}
// Above never returned true, so return false
return memo[n] = false;
}
private boolean recursive(int n) {
// Think like alice started the game.
// base case
if (n == 1) {
return false; // aiice loses when n = 1
}
// try all 0 < x < n
for (int x = 1; x < n; x++) {
if (n % x == 0) {
// below f(n-x) is Bob turn
// f(n) returns true if someone wins.
// so if below function returns false Alice wins
// if bob loses from any of moves, alice wins
if (!recursive(n - x)) {
return true;
}
}
}
// Above never returned true, so return false
return false;
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.divisorGame(2));
System.out.println(s.divisorGame(3));
System.out.println(s.divisorGame(4));
System.out.println(s.divisorGame(5));
System.out.println(s.divisorGame(6));
}
}
š Key Insights
Function Definitions Matter:
CLEAR: divisorGame(n) = "Does player at position n win?"
VAGUE: divisorGame(n) = "something about n"
dp[i] = "Does player at position i win?"
true = wins
false = loses
Without clear definition ā confusion! ā ļø
With clear definition ā easy to understand! ā
Base Case:
dp[1] = false (cannot make any move)
WHY?
At position 1, need x where (0 < x < 1) AND (1 % x == 0)
No such x exists!
Cannot move ā lose
divisorGame(1) = false
Meaning: "Player at position 1 loses"
Game Theory Recurrence:
dp[i] = true if EXISTS valid move to losing position
dp[i] = false if ALL moves lead to winning positions
Valid move: (0 < x < i) AND (i % x == 0) AND dp[i-x] = false
WHY?
"I win if I can make opponent lose"
"I lose if opponent wins no matter what I do"
Classic minimax pattern! šÆ
Mathematical Pattern:
EVEN ā true (Alice wins)
ODD ā false (Alice loses)
WHY?
EVEN: Can choose x=1 ā opponent gets ODD ā opponent loses
ODD: All divisors are ODD ā opponent gets EVEN ā opponent wins
Proven by mathematical induction! ā
Memoization Note:
Use Boolean[] not boolean[]
WHY?
Boolean can be null ā "not calculated"
boolean defaults to false ā can't distinguish!
memo[i] states:
- null: not yet calculated
- true: position i wins
- false: position i loses
šŖ Memory Aid
"Even is heaven, odd is not!"
"Player at position asks: do I win?"
"dp[i] means: position i wins or loses!"
"Math beats DP - check parity!" āØ
ā ļø Common Mistakes
- ā Confusing what dp[i] represents (position vs player)
- ā Using boolean[] instead of Boolean[] in memoization
- ā Including n itself as valid divisor (must be x < n)
- ā Thinking DP is required (pattern solution is O(1)!)
- ā Not understanding function return value meaning
ā Interview Talking Points
"This is a game theory problem with optimal play.
Function Definition:
divisorGame(n) = Does the player at position n win?
dp[i] = Does the player at position i win?
Clear definition prevents confusion!
DP Approach:
1. Base case: dp[1] = false (cannot move, loses)
2. Recurrence: dp[i] = true if any move leads to dp[j]=false
3. Try all divisors x of i, check dp[i-x]
4. Build from 1 to n
Pattern Discovery:
After solving with DP, notice: EVEN wins, ODD loses
Mathematical Proof:
- EVEN: choose x=1 ā opponent gets ODD ā wins
- ODD: all divisors odd ā opponent gets EVEN ā loses
Optimal Solution:
return n % 2 == 0; // O(1) time and space!
Why DP still useful:
- Systematic approach to discover pattern
- Clear function definitions aid understanding
- Works for variations without closed formula
Complexity: DP is O(n²) time, O(n) space
Pattern is O(1) time, O(1) space ā"
š Congratulations!
You've mastered Divisor Game with crystal-clear function definitions!
What You Learned:
ā
Function Definition Clarity - Knowing what parameters and returns mean
ā
Game Theory DP - Win if opponent loses, lose if opponent wins
ā
Minimax Pattern - Optimal play for both players
ā
Boolean Memoization - Using Boolean[] for null state
ā
Pattern Discovery - Even/odd mathematical insight
ā
Proof by Induction - Mathematical correctness guarantee
ā
O(1) Optimization - From O(n²) to constant time!
The Power of Clear Definitions:
WITHOUT clear function definition:
"What does divisorGame(5) return?" š¤
"Does it mean Alice wins or current player wins?" š¤
"What about dp[i]? What does it represent?" š¤
WITH clear function definition:
divisorGame(5) = "Does player at position 5 win?"
Return: false (player at 5 loses)
dp[i] = "Does player at position i win?"
Crystal clear! No ambiguity! āšÆ
Pattern Evolution:
Problem 205: Counting (Fibonacci)
ways(n) = ways(n-1) + ways(n-2)
Definition: "Number of ways to reach step n"
Problem 206: Optimization (Min cost)
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
Definition: "Minimum cost to reach step i"
Problem 207: Game Theory (Minimax)
dp[i] = true if EXISTS move to dp[j]=false
Definition: "Does player at position i win?"
Clear definitions for ALL! šŖ
You've now mastered function definitions in DP! This clarity will help in ALL future problems! šāØ