Intuition¶
This is an impartial game, so each pile size i is either a winning position
(the player to move can force a win) or a losing one. A position is winning if
there exists some move to a losing position for the opponent. The only moves are
removing a perfect square j*j, so i is winning when any i - j*j is a losing
position.
Approach: Dynamic Programming (Game States)¶
Let dp[i] be true if the player to move on a pile of i stones wins with
optimal play.
- Base case:
dp[0] = false— no move available, so the player to move loses. - Transition: for each
i, try every squarej*j <= i. If somedp[i - j*j]isfalse(leaving the opponent in a losing position), thendp[i] = true.
The answer is dp[n] (Alice moves first). The Go version ORs across all squares;
the Rust version breaks early on the first winning move.
Complexity¶
- Time complexity: $$O(n \sqrt{n})$$, since for each of the
nstates we try up to $$\sqrt{i}$$ square moves. - Space complexity: $$O(n)$$ for the
dparray.
Code¶
Go¶
func winnerSquareGame(n int) bool {
dp := make([]bool, n + 1)
for i := 1; i <= n; i++ {
for j := 1; j * j <= i; j++ {
dp[i] = dp[i] || (!dp[i - j * j])
}
}
return dp[n]
}