Problem
Alice and Bob continue their games with piles of stones. There are a number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]
. The objective of the game is to end with the most stones.
Alice and Bob take turns, with Alice starting first. Initially, M = 1
.
On each player’s turn, that player can take all the stones in the first X
remaining piles, where 1 <= X <= 2M
. Then, we set M = max(M, X)
.
The game continues until all the stones have been taken.
Assuming Alice and Bob play optimally, return the maximum number of stones Alice can get.
Examples
Example 1:
Input: piles = [2,7,9,4,4]
Output: 10
Explanation: If Alice takes one pile at the beginning, Bob takes two piles, then Alice takes 2 piles again. Alice can get 2 + 4 + 4 = 10 piles in total. If Alice takes two piles at the beginning, then Bob can take all three piles left. In this case, Alice get 2 + 7 = 9 piles in total. So we return 10 since it's larger.
Example 2:
Input: piles = [1,2,3,4,5,100]
Output: 104
Solution
Method 1 - By minimizing the stones returned by Bob
The game involves Alice and Bob playing optimally to collect as many stones as possible from a row of piles. Alice always plays first, and the players take turns. The key constraints are:
Each player can take any number of piles between 1 and 2M, where M is initially set to 1 and adjusts based on the moves.
The goal is to maximize the number of stones collected by Alice.
Prefix Sums:
- We use
prefixSums
to quickly calculate the sum of stones from any subarray. prefixSums[i]
stores the total number of stones from the start up to the i-th pile.
- We use
Memoization Table:
memo[i][M]
stores the maximum number of stones Alice can collect starting from indexi
with the parameterM
.- This avoids recalculating results for the same state multiple times.
Recursive Function:
- The helper function
findMaxStones
handles the game logic recursively. - It ensures that both players play optimally, and calculates Alice’s maximum possible stones.
- The helper function
This is how recursion will work:
- The goal is to maximize Alice’s collected stones. For each possible number of piles
X
Alice can take (from 1 to2 * M
):- Calculate the total stones Alice can collect by summing the stones from index
i
toi + X - 1
. - Recursively compute the result for the remaining piles starting from index
i + X
, and adjustM
tomax(M, X)
. - Deduct the maximum stones Bob can collect in his turn from the total stones Alice can collect.
- Calculate the total stones Alice can collect by summing the stones from index
Video Explanation
Here is the video explanation:
Code
Java
class Solution {
private int[] piles;
private int[] prefixSums;
private int n;
private int[][] memo;
public int stoneGameII(int[] piles) {
this.piles = piles;
this.n = piles.length;
this.memo = new int[n][n + 1];
// Initialize memo table to -1 indicating uncomputed states
for (int[] row : memo) {
Arrays.fill(row, -1);
}
// Calculate prefix sums
int[] prefixSums = new int[n + 1];
for (int i = 0; i < n; i++) {
prefixSums[i + 1] = prefixSums[i] + piles[i];
}
this.prefixSums = prefixSums;
return findMaxStones(0, 1);
}
private int findMaxStones(int i, int M) {
if (i >= n) {
return 0;
}
if (memo[i][M] != -1) {
return memo[i][M];
}
int maxStones = 0;
int total = 0;
for (int X = 1; X <= 2 * M && i + X <= n; X++) {
// Using prefix sums to calculate the sum of stones taken by Alice
int currentStones = prefixSums[i + X] - prefixSums[i];
int totalStonesRemaining = prefixSums[n] - prefixSums[i + X];
// The result of the remaining game
int nextStones = findMaxStones(i + X, Math.max(M, X));
// Alice's optimal stones after Bob's turn
maxStones = Math.max(maxStones, currentStones + totalStonesRemaining - nextStones);
}
memo[i][M] = maxStones;
return maxStones;
}
}
Complexity
- Time:
O(n^3)
- Because at each recursive call,
i
goes from 0 to n - But another parameter is
m
and in worst case it can go ton/2
- Then, we are running the inner loop insider function from X = 1 to n in worst case
- Because at each recursive call,
- Space:
O(n^2)
using recursion stack and memo table
Method 2 - By maximizing the difference
Code
Java
public class Solution {
private int[] piles;
private int[] prefixSums;
private int n;
private int[][] memo;
public int stoneGameII(int[] piles) {
this.piles = piles;
this.n = piles.length;
this.memo = new int[n][n + 1];
// Initialize memo table to Integer.MIN_VALUE indicating uncomputed
// states
for (int[] row : memo) {
Arrays.fill(row, Integer.MIN_VALUE);
}
// Calculate prefix sums
this.prefixSums = new int[n + 1];
for (int i = 0; i < n; i++) {
prefixSums[i + 1] = prefixSums[i] + piles[i];
}
// Calculate the maximum difference between Alice's and Bob's stones,
// starting at index 0 with M = 1
int maxDifference = findMaxDifference(0, 1);
// Calculate the total stones using the prefix sums
int totalStones = prefixSums[n];
// Calculate Alice's stones based on the maximum difference
int aliceStones = (totalStones + maxDifference) / 2;
return aliceStones;
}
private int findMaxDifference(int i, int M) {
if (i >= n) {
return 0;
}
if (memo[i][M] != Integer.MIN_VALUE) {
return memo[i][M];
}
int maxDiff = Integer.MIN_VALUE;
for (int X = 1; X <= 2 * M && i + X <= n; X++) {
int currentStones = prefixSums[i + X] - prefixSums[i];
int nextDiff = findMaxDifference(i + X, Math.max(M, X));
maxDiff = Math.max(maxDiff, currentStones - nextDiff);
}
memo[i][M] = maxDiff;
return maxDiff;
}
public static void main(String[] args) {
Solution game = new Solution();
int[] piles = {2, 7, 9, 4, 4};
System.out.println(game.stoneGameII(piles)); // Expected Output: 10
}
}
Complexity
- Time:
O(NNNXXXNNN)
- Space:
O(NNNXXX)