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.

Examples

Example 1

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

Example 2

1
2
3
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

1
2
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

  • n == stones.length
  • 2 <= n <= 1000
  • 1 <= stones[i] <= 1000

Solution

Method 1 – Dynamic Programming (Game Theory)

Intuition

This is a classic take-away game. For each number of stones, we check if there is a move (removing a square number) that leaves the opponent in a losing position. If so, the current player can win.

Approach

  1. Let dp[i] be true if the current player can win with i stones.
  2. For each i, try all possible square numbers k*k <= i. If there is any dp[i - k*k] == false, then dp[i] = true.
  3. The answer is dp[n].

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    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];
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func winnerSquareGame(n int) bool {
    dp := make([]bool, n+1)
    for i := 1; i <= n; i++ {
        for k := 1; k*k <= i; k++ {
            if !dp[i-k*k] {
                dp[i] = true
                break
            }
        }
    }
    return dp[n]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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];
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    fun winnerSquareGame(n: Int): Boolean {
        val dp = BooleanArray(n+1)
        for (i in 1..n) {
            for (k in 1..Math.sqrt(i.toDouble()).toInt()) {
                if (!dp[i - k*k]) {
                    dp[i] = true
                    break
                }
            }
        }
        return dp[n]
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    def winnerSquareGame(self, n: int) -> bool:
        dp = [False] * (n+1)
        for i in range(1, n+1):
            k = 1
            while k*k <= i:
                if not dp[i - k*k]:
                    dp[i] = True
                    break
                k += 1
        return dp[n]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
impl Solution {
    pub fn winner_square_game(n: i32) -> bool {
        let n = n as usize;
        let mut dp = vec![false; n+1];
        for i in 1..=n {
            let mut k = 1;
            while k*k <= i {
                if !dp[i - k*k] {
                    dp[i] = true;
                    break;
                }
                k += 1;
            }
        }
        dp[n]
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    winnerSquareGame(n: number): boolean {
        const dp = 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];
    }
}

Complexity

  • ⏰ Time complexity: O(n*sqrt(n)) where n is the number of stones.
  • 🧺 Space complexity: O(n) for the DP array.