Stone Game VII
MEDIUMDescription
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, they can remove either the leftmost stone or the rightmost stone from the row and receive points equal to the sum of the remaining stones' values in the row. The winner is the one with the higher score when there are no stones left to remove.
Bob found that he will always lose this game (poor Bob, he always loses), so he decided to minimize the score's difference. Alice's goal is to maximize the difference in the score.
Given an array of integers stones where stones[i] represents the value of the ith stone from the left, return the difference in Alice and Bob's score if they both play optimally.
Example 1:
Input: stones = [5,3,1,4,2] Output: 6 Explanation: - Alice removes 2 and gets 5 + 3 + 1 + 4 = 13 points. Alice = 13, Bob = 0, stones = [5,3,1,4]. - Bob removes 5 and gets 3 + 1 + 4 = 8 points. Alice = 13, Bob = 8, stones = [3,1,4]. - Alice removes 3 and gets 1 + 4 = 5 points. Alice = 18, Bob = 8, stones = [1,4]. - Bob removes 1 and gets 4 points. Alice = 18, Bob = 12, stones = [4]. - Alice removes 4 and gets 0 points. Alice = 18, Bob = 12, stones = []. The score difference is 18 - 12 = 6.
Example 2:
Input: stones = [7,90,5,1,100,10,10,2] Output: 122
Constraints:
n == stones.length2 <= n <= 10001 <= stones[i] <= 1000
Approaches
Checkout 4 different approaches to solve Stone Game VII. Click on different approaches to view the approach and algorithm in detail.
Space-Optimized Bottom-Up Dynamic Programming
This approach optimizes the space complexity of the bottom-up DP solution. By observing the state transitions, we can see that to compute the values for a subarray of a certain length, we only need the results for subarrays of the immediately smaller length. This means to compute the current row i of our DP table, we only need the next row i+1. This dependency allows us to reduce the space from O(n^2) to O(n).
Algorithm
- Create a prefix sum array for
O(1)sum calculations. - Create a 1D DP array
dp[n], initialized to 0. - Iterate the start index
ifromn-2down to0. - In the inner loop, iterate the end index
jfromi+1up ton-1. - Inside the inner loop, calculate the two possible differences:
a.
diff1 = (prefixSum[j+1] - prefixSum[i+1]) - dp[j]. Heredp[j]holds the value from the previous outer loop, which corresponds todp[i+1][j]. b.diff2 = (prefixSum[j] - prefixSum[i]) - dp[j-1]. Heredp[j-1]holds the value just computed in the current inner loop, which corresponds todp[i][j-1]. - Update
dp[j]withmax(diff1, diff2). - After the loops,
dp[n-1]will contain the result for the entire arraystones[0...n-1].
The recurrence is dp[i][j] = max(sum(i+1, j) - dp[i+1][j], sum(i, j-1) - dp[i][j-1]). Let's iterate by the starting index i from n-2 down to 0, and the ending index j from i+1 up to n-1. When we compute dp[i][j], we need dp[i+1][j] (from the 'next' row i+1) and dp[i][j-1] (from the 'current' row i, but a previous column). This structure allows for space optimization. We can use a 1D array, say dp[n]. Let dp[j] represent the value for the current row i and column j. When we are calculating the row for i, the dp array will hold the values from the previous iteration, which corresponds to row i+1. The recurrence becomes: dp[j] = max(sum(i+1, j) - dp[j], sum(i, j-1) - dp[j-1]), where we update the dp array in place.
class Solution {
public int stoneGameVII(int[] stones) {
int n = stones.length;
int[] prefixSum = new int[n + 1];
for (int i = 0; i < n; i++) {
prefixSum[i + 1] = prefixSum[i] + stones[i];
}
int[] dp = new int[n];
// i is the starting index of the subarray
for (int i = n - 2; i >= 0; i--) {
// j is the ending index of the subarray
for (int j = i + 1; j < n; j++) {
// Sum of stones from i+1 to j
int sumTakeLeft = prefixSum[j + 1] - prefixSum[i + 1];
// Sum of stones from i to j-1
int sumTakeRight = prefixSum[j] - prefixSum[i];
// dp[j] currently holds dp[i+1][j] from the previous outer loop iteration
// dp[j-1] holds dp[i][j-1] from the current inner loop iteration
int diff1 = sumTakeLeft - dp[j];
int diff2 = sumTakeRight - dp[j - 1];
dp[j] = Math.max(diff1, diff2);
}
}
return dp[n - 1];
}
}
Complexity Analysis
Pros and Cons
- Most efficient in terms of space complexity.
- Maintains the optimal O(n^2) time complexity.
- The logic for in-place updates and loop directions can be tricky to reason about compared to the O(n^2) space solution.
Code Solutions
Checking out 3 solutions in different languages for Stone Game VII. Click on different languages to view the code.
class Solution {
private
int[] s;
private
Integer[][] f;
public
int stoneGameVII(int[] stones) {
int n = stones.length;
s = new int[n + 1];
f = new Integer[n][n];
for (int i = 0; i < n; ++i) {
s[i + 1] = s[i] + stones[i];
}
return dfs(0, n - 1);
}
private
int dfs(int i, int j) {
if (i > j) {
return 0;
}
if (f[i][j] != null) {
return f[i][j];
}
int a = s[j + 1] - s[i + 1] - dfs(i + 1, j);
int b = s[j] - s[i] - dfs(i, j - 1);
return f[i][j] = Math.max(a, b);
}
}
Video Solution
Watch the video walkthrough for Stone Game VII
Similar Questions
5 related questions you might find useful
Patterns:
Data Structures:
Companies:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.