1510. Stone Game IV
SourceBiweekly Contest 30 Q4DifficultyHardRating1786
Description
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
Solutions
Solution 1: Memoization
Thinking
Players alternately remove a square number of stones from a pile of \(n\); the one who takes the last stone wins. \(n\le 10^5\), so expanding the full game tree would recompute the same remainder many times.
A position depends only on the remaining count: it is winning if some move leaves the opponent in a losing position. Memoized \(\textit{dfs}(i)\) answers whether the player to move wins with \(i\) stones, trying every \(j^2\le i\). Each state has \(O(\sqrt{i})\) transitions.
We design a function \(\textit{dfs}(i)\), which represents whether the current player can win the game when there are \(i\) stones in the pile. If the current player can win, it returns \(\textit{true}\); otherwise, it returns \(\textit{false}\). The answer is \(\textit{dfs}(n)\).
The calculation process of the function \(\textit{dfs}(i)\) is as follows:
- If \(i \leq 0\), it means the current player cannot make any move, so the current player loses the game, return \(\textit{false}\);
- Otherwise, enumerate the number of stones \(j\) that the current player can take away, where \(j\) is a square number. If the other player cannot win the game after the current player takes away \(j\) stones, then the current player wins the game, return \(\textit{true}\). If all \(j\) enumerated cannot satisfy the above condition, then the current player loses the game, return \(\textit{false}\).
To avoid repeated calculations, we can use memoization, i.e., use an array \(f\) to record the calculation results of the function \(\textit{dfs}(i)\).
The time complexity is \(O(n \sqrt{n})\), and the space complexity is \(O(n)\), where \(n\) is the number of stones in the pile.
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
Solution 2: Dynamic Programming
Thinking
Solution 1 already fills this win/loss table from fewer stones to more. The \(f[i]\) below is that same table.
Solution 1 already fills this table from fewer stones to more. Define an array \(f\), where \(f[i]\) represents whether the current player can win the game when there are \(i\) stones in the pile. If the current player can win, then \(f[i]\) is \(\textit{true}\), otherwise it is \(\textit{false}\). The answer is \(f[n]\).
We enumerate \(i\) in the range \([1,..n]\), and enumerate \(j\) in the range \([1,..i]\), where \(j\) is a square number. If the other player cannot win the game after the current player takes away \(j\) stones, then the current player wins the game, i.e., \(f[i] = \textit{true}\). If all \(j\) enumerated cannot satisfy the above condition, then the current player loses the game, i.e., \(f[i] = \textit{false}\). Therefore, we can get the state transition equation:
Finally, we return \(f[n]\).
The time complexity is \(O(n \sqrt{n})\), and the space complexity is \(O(n)\), where \(n\) is the number of stones in the pile.
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Solution 3: Dynamic Programming
Thinking
Expanding the whole game tree is exponential. There can be \(10^5\) stones, and the first move always removes one stone, so the search has depth \(n\). Whether a position is winning depends only on the remaining count, and only on smaller counts. Let \(f[i]\) record a win with \(i\) stones, with \(f[0]\) losing, and scan \(i\) upward. A square \(j^2\) that leaves a losing position makes \(f[i]\) winning.
Let \(f[i]\) be whether the player to move wins with \(i\) stones. \(f[0]\) is \(\textit{false}\), since no move remains. For \(i\) from \(1\) to \(n\), enumerate squares \(j^2 \le i\). If \(f[i-j^2]\) is \(\textit{false}\), the opponent loses from there, so \(f[i]\) is \(\textit{true}\) and the remaining squares can be skipped. If every square leaves a winning position for the opponent, \(f[i]\) stays \(\textit{false}\). The answer is \(f[n]\).
The time complexity is \(O(n \sqrt{n})\), and the space complexity is \(O(n)\), where \(n\) is the number of stones in the pile.
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |