Talentd
FleetCode
Make and follow sheets.Sign in

Stone Game IV solution and explanation

HardDynamic ProgrammingGame TheoryMath4 min read65.2% acceptanceUpdated 9 Apr 2026
Leetcode 1510. Stone Game IVFraz · 11:06 · 7K viewsWatch on YouTube
9 more videos on this problem
  1. Leetcode 1510. Stone Game IVFraz · 11:06 · 7K views
  2. Stone Game IV Leetcode 1510. Dynamic Programming Code C++ + ExplanationCode with Alisha · 11:17 · 2K views
  3. Stone Game IV | Leetcode 1510 | Game Theory | DPAyushi Sharma · 11:27 · 2K views
  4. Stone Game IV | Live Coding with Explanation | Leetcode - 1510Algorithms Made Easy · 14:18 · 1K views
  5. 1510. Stone Game IV (Leetcode Problem)viralSongs · 6:35 · 947 views
  6. Stone Game IV | Leetcode 1510 |Live coding session 🔥🔥🔥🔥 | Dynamic ProgrammingCoding Decoded · 12:12 · 866 views
  7. Stone Game IV | Leet code 1510 | Theory explained + Python codeSai Anish Malla · 19:10 · 703 views
  8. Stone Game 4 Leetcode | Leetcode 1510 | Recursive + Top Down + Bottom Up Approach explainedCode Bashers · 14:20 · 373 views
  9. Daily Leetcode #661 part 2: Jan 21, 2022 - 1510. Stone Game IVProgramming Live with Larry · 12:50 · 335 views
  10. Leetcode - Stone Game IV (Python)Timothy H Chang · 6:20 · 302 views

The problem

Alice and Bob take turns playing a game, with Alice starting first.

Initially, there are n stones in a pile. On each player's turn, that player makes a move consisting of removing any non-zero square number of stones in the pile.

Also, if a player cannot make a move, he/she loses the game.

Given a positive integer n, return true if and only if Alice wins the game otherwise return false, assuming both players play optimally.

Example 1:

Input: n = 1
Output: true
Explanation: Alice can remove 1 stone winning the game because Bob doesn't have any moves.

Example 2:

Input: n = 2
Output: false
Explanation: Alice can only remove 1 stone, after that Bob removes the last one winning the game (2 -> 1 -> 0).

Example 3:

Input: n = 4
Output: true
Explanation: n is already a perfect square, Alice can win with one move, removing 4 stones (4 -> 0).

Constraints:

  • 1 <= n <= 105

Approach overview

Problem Overview: You are given n stones. Two players take turns removing a non‑zero square number of stones (1, 4, 9, 16, ...). The player who cannot make a move loses. Determine whether Alice, who plays first, can force a win if both players play optimally.

Approach 1: Recursive with Memoization (Time: O(n√n), Space: O(n))

This problem fits the classic win/lose state model used in game theory. From a state with n stones, try removing every square number i*i where i*i ≤ n. If any move leads the opponent to a losing state, the current state is winning. A plain recursive search recomputes the same states repeatedly, so store results in a memo table keyed by n. Each state explores at most √n moves and each state is computed once. This reduces the exponential search tree to O(n√n) time while using O(n) memory for memoization.

Approach 2: Dynamic Programming (Bottom-Up) (Time: O(n√n), Space: O(n))

The same win/lose logic can be implemented iteratively using dynamic programming. Define dp[i] as whether the current player wins with i stones remaining. Initialize dp[0] = false since no moves are possible. For each i from 1 to n, iterate over all perfect squares j*j ≤ i. If dp[i - j*j] is false, removing j*j stones forces the opponent into a losing position, so mark dp[i] = true. Each state checks up to √i transitions, leading to overall complexity O(n√n) with O(n) space.

The key insight: a position is winning if at least one move leads to a losing position for the opponent. This pattern appears frequently in impartial combinatorial games and many math and DP problems.

Recommended for interviews: The bottom‑up dynamic programming solution is what most interviewers expect. It demonstrates that you can model the game as a state transition problem and reason about winning vs losing states. Starting with the recursive formulation helps show your thinking, but converting it into iterative DP proves you understand optimization and avoids recursion overhead.

Approach 1: Dynamic Programming Approach

This approach uses Dynamic Programming to determine if Alice can win given n stones. We can maintain a boolean array dp where dp[i] represents whether Alice can win with i stones. A state is winning if there's any move that leaves the opponent in a losing state.

import math

def winnerSquareGame(n):
    dp = [False] * (n + 1)
    for i in range(1, n + 1):
        for k in range(1, int(math.sqrt(i)) + 1):
            if not dp[i - k * k]:
                dp[i] = True
                break
    return dp[n]
import java.util.*;

public class Solution {
    public boolean winnerSquareGame(int n) {
        boolean[] dp = new boolean[n + 1];
        for (int i = 1; i <= n; i++) {
            for (int k = 1; k * k <= i; k++) {
                if (!dp[i - k * k]) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[n];
    }
}
#include <vector>
#include <cmath>
using namespace std;

bool winnerSquareGame(int n) {
    vector<bool> dp(n + 1, false);
    for (int i = 1; i <= n; ++i) {
        for (int k = 1; k * k <= i; ++k) {
            if (!dp[i - k * k]) {
                dp[i] = true;
                break;
            }
        }
    }
    return dp[n];
}
var winnerSquareGame = function(n) {
    const dp = new Array(n + 1).fill(false);
    for (let i = 1; i <= n; i++) {
        for (let k = 1; k * k <= i; k++) {
            if (!dp[i - k * k]) {
                dp[i] = true;
                break;
            }
        }
    }
    return dp[n];
};
#include <stdbool.h>
#include <math.h>

bool winnerSquareGame(int n) {
    bool dp[n+1];
    for (int i = 0; i <= n; ++i) {
        dp[i] = false;
    }
    for (int i = 1; i <= n; ++i) {
        for (int k = 1; k*k <= i; ++k) {
            if (!dp[i-k*k]) {
                dp[i] = true;
                break;
            }
        }
    }
    return dp[n];
}
using System;

public class Solution {
    public bool WinnerSquareGame(int n) {
        bool[] dp = new bool[n + 1];
        for (int i = 1; i <= n; i++) {
            for (int k = 1; k * k <= i; k++) {
                if (!dp[i - k * k]) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[n];
    }
}

We initialize a boolean list dp and iterate through each state, marking it as winning if there exists a move that forces the opponent into a losing position.

Complexity

Time Complexity: O(n*sqrt(n)), Space Complexity: O(n)

Try this approach in the editor

Approach 2: Recursive with Memoization

This approach involves a recursive solution with memoization to cache previously computed results. If n is already solved, we return the cached result. Otherwise, we recursively check if any move leaves the opponent in a losing position.

def winnerSquareGame(n):
    memo = [-1] * (n + 1)
    def isWinning(x):
        if x == 0:
            return False
        if memo[x] != -1:
            return memo[x]
        for k in range(1, int(x ** 0.5) + 1):
            if not isWinning(x - k*k):
                memo[x] = True
                return True
        memo[x] = False
        return False
    return isWinning(n)
import java.util.*;

public class Solution {
    public boolean winnerSquareGame(int n) {
        Boolean[] memo = new Boolean[n + 1];
        return isWinning(n, memo);
    }

    private boolean isWinning(int n, Boolean[] memo) {
        if (n == 0) return false;
        if (memo[n] != null) return memo[n];
        for (int k = 1; k * k <= n; ++k) {
            if (!isWinning(n - k * k, memo)) {
                return memo[n] = true;
            }
        }
        return memo[n] = false;
    }
}
#include <vector>
#include <cmath>

bool isWinning(int n, std::vector<int>& memo) {
    if (n == 0) return false;
    if (memo[n] != -1) return memo[n];
    for (int k = 1; k * k <= n; ++k) {
        if (!isWinning(n - k * k, memo)) {
            return memo[n] = true;
        }
    }
    return memo[n] = false;
}

bool winnerSquareGame(int n) {
    std::vector<int> memo(n+1, -1);
    return isWinning(n, memo);
}
var winnerSquareGame = function(n) {
    let memo = new Array(n + 1).fill(-1);
    const isWinning = function(x) {
        if (x === 0) return false;
        if (memo[x] !== -1) return memo[x];
        for (let k = 1; k * k <= x; k++) {
            if (!isWinning(x - k * k)) {
                memo[x] = true;
                return true;
            }
        }
        memo[x] = false;
        return false;
    }
    return isWinning(n);
};
#include <stdbool.h>
#include <math.h>
#include <string.h>

bool isWinning(int n, bool *memo) {
    if (n == 0) return false;
    if (memo[n] != -1) return memo[n];
    for (int k = 1; k * k <= n; ++k) {
        if (!isWinning(n - k * k, memo)) {
            return memo[n] = true;
        }
    }
    return memo[n] = false;
}

bool winnerSquareGame(int n) {
    bool memo[n+1];
    memset(memo, -1, sizeof(memo));
    return isWinning(n, memo);
}
using System;

public class Solution {
    public bool WinnerSquareGame(int n) {
        int?[] memo = new int?[n + 1];
        return IsWinning(n, memo);
    }

    private bool IsWinning(int n, int?[] memo) {
        if (n == 0) return false;
        if (memo[n] != null) return memo[n] == 1;
        for (int k = 1; k * k <= n; ++k) {
            if (!IsWinning(n - k * k, memo)) {
                memo[n] = 1;
                return true;
            }
        }
        memo[n] = 0;
        return false;
    }
}

This Python function defines a helper to recursively check if there's a move that forces the opponent into a losing state. Previously computed results are stored in memo, enhancing efficiency through reduced redundant calculations.

Complexity

Time Complexity: O(n*sqrt(n)), Space Complexity: O(n)

Try this approach in the editor

Approach 3: Default Approach

function winnerSquareGame(n: number): boolean {
    const f: number[] = new Array(n + 1).fill(0);
    const dfs = (i: number): boolean => {
        if (i <= 0) {
            return false;
        }
        if (f[i] !== 0) {
            return f[i] === 1;
        }
        for (let j = 1; j * j <= i; ++j) {
            if (!dfs(i - j * j)) {
                f[i] = 1;
                return true;
            }
        }
        f[i] = -1;
        return false;
    };
    return dfs(n);
}
func winnerSquareGame(n int) bool {
	f := make([]int, n+1)
	var dfs func(int) bool
	dfs = func(i int) bool {
		if i <= 0 {
			return false
		}
		if f[i] != 0 {
			return f[i] == 1
		}
		for j := 1; j <= i/j; j++ {
			if !dfs(i - j*j) {
				f[i] = 1
				return true
			}
		}
		f[i] = -1
		return false
	}
	return dfs(n)
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming Approach

Time Complexity: O(n*sqrt(n)), Space Complexity: O(n)

Recursive with Memoization

Time Complexity: O(n*sqrt(n)), Space Complexity: O(n)

Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Naive RecursionExponentialO(n)Conceptual starting point to understand win/lose states in the game tree
Recursive with MemoizationO(n√n)O(n)Top‑down reasoning when you want a clear recursive definition of the game state
Dynamic Programming (Bottom-Up)O(n√n)O(n)Preferred interview solution; efficient iterative computation of all states

Frequently asked questions

Is Stone Game IV easy or hard?

Stone Game IV is classified as Hard on LeetCode even though the implementation is short. The difficulty comes from recognizing the game theory pattern and modeling the problem as DP with winning and losing states.

Stone Game IV Python/Java solution

Both Python and Java implementations typically use a DP array of size n+1. For each index i, iterate through j*j ≤ i and mark dp[i] true if dp[i - j*j] is false. The same logic works across languages including C++, JavaScript, and C#.

How to solve Stone Game IV in O(n)?

A strict O(n) solution is not commonly used for this problem because each state must consider multiple square moves. The standard optimal approach checks all perfect squares ≤ n for every state, resulting in O(n√n) time using dynamic programming.

What is the best approach for Stone Game IV?

Dynamic Programming is the most reliable approach. Define dp[i] as whether the current player wins with i stones. For each i, try removing every square number j*j ≤ i and check if dp[i - j*j] is false. If such a move exists, dp[i] becomes true. This runs in O(n√n) time with O(n) space.

Is Stone Game IV asked at Google/Amazon/Meta?

Stone Game style problems frequently appear in interviews at companies like Google, Amazon, and Meta because they test reasoning about game states, recursion, and dynamic programming. Variants of this problem are commonly used to evaluate understanding of optimal play strategies.

What data structure is used in Stone Game IV?

The core data structure is a boolean DP array or memoization map that stores whether a state with i stones is winning or losing. The algorithm iterates through perfect squares and performs constant-time lookups in this table.

What is the time complexity of Stone Game IV?

The optimal solution runs in O(n√n) time. For each state from 1 to n, the algorithm iterates through all square numbers up to √n. The space complexity is O(n) for storing the DP or memoization table.

Comments

Now write it yourselfClose this page and solve Stone Game IV in the editor, without looking.
Practise Stone Game IV