Alice and Bob take turns playing a game, with Alice starting first.
There are n
stones arranged in a row. On each player's turn, while the number of stones is more than one, they will do the following:
x > 1
, and remove the leftmost x
stones from the row.The game stops when only one stone is left in the row.
The score difference between Alice and Bob is (Alice's score - Bob's score)
. Alice's goal is to maximize the score difference, and Bob's goal is the minimize the score difference.
Given an integer array stones
of length n
where stones[i]
represents the value of the ith
stone from the left, return the score difference between Alice and Bob if they both play optimally.
Example 1:
Input: stones = [-1,2,-3,4,-5] Output: 5 Explanation: - Alice removes the first 4 stones, adds (-1) + 2 + (-3) + 4 = 2 to her score, and places a stone of value 2 on the left. stones = [2,-5]. - Bob removes the first 2 stones, adds 2 + (-5) = -3 to his score, and places a stone of value -3 on the left. stones = [-3]. The difference between their scores is 2 - (-3) = 5.
Example 2:
Input: stones = [7,-6,5,10,5,-2,-6] Output: 13 Explanation: - Alice removes all stones, adds 7 + (-6) + 5 + 10 + 5 + (-2) + (-6) = 13 to her score, and places a stone of value 13 on the left. stones = [13]. The difference between their scores is 13 - 0 = 13.
Example 3:
Input: stones = [-10,-12] Output: -22 Explanation: - Alice can only make one move, which is to remove both stones. She adds (-10) + (-12) = -22 to her score and places a stone of value -22 on the left. stones = [-22]. The difference between their scores is (-22) - 0 = -22.
Constraints:
n == stones.length
2 <= n <= 105
-104 <= stones[i] <= 104
This problem involves a two-player game with optimal strategies. The core idea is to leverage dynamic programming or memoization to efficiently explore the decision tree of possible moves. The key observation is that the game's state can be effectively summarized using a prefix sum.
Prefix Sum: Calculate the prefix sum of the stones
array. This allows us to quickly compute the sum of any contiguous subarray. The prefix sum array s
where s[i]
is the sum of stones[0...i]
.
Dynamic Programming (or Memoization): We work backward from the end of the game.
Base Case: When only one stone remains (i == n - 1
), the score difference is simply the value of that stone (s[n-1]
).
Recursive Relation: For a given state with i
stones remaining, the current player has two choices:
s[i]
(the sum of all remaining stones) minus the score difference the other player will get in the next turn (which we'll already have calculated).Iteration: Instead of recursion (which can be less efficient due to function call overhead), we can iterate through the prefix sum array in reverse order to fill our DP table from the base case.
Result: The final result is the maximum score difference Alice can achieve starting from the initial state.
from itertools import accumulate
class Solution:
def stoneGameVIII(self, stones: List[int]) -> int:
n = len(stones)
s = list(accumulate(stones)) # Prefix sum
dp = s[-1] # Base case: only one stone left
for i in range(n - 2, 0, -1):
dp = max(dp, s[i] - dp) # DP relation
return dp
import java.util.Arrays;
class Solution {
public int stoneGameVIII(int[] stones) {
int n = stones.length;
int[] prefixSum = new int[n];
prefixSum[0] = stones[0];
for (int i = 1; i < n; i++) {
prefixSum[i] = prefixSum[i - 1] + stones[i];
}
int dp = prefixSum[n - 1]; //Base case
for (int i = n - 2; i > 0; i--) {
dp = Math.max(dp, prefixSum[i] - dp); //DP relation
}
return dp;
}
}
This iterative dynamic programming solution is highly efficient and avoids unnecessary recursion. The use of prefix sums makes the calculation of the score difference for any subset of stones very fast. The overall time complexity is linear, making it suitable for large input sizes.