Approach 1: Brute Force -> Top Down memoization
- A key intuition for this problem is to realize that you dont need to keep track of max_stones of Bob, but instead you need to minimize Alice's max_stones in Bob's turn. The reason why this work is because if we reduce the number of stones Alice can take, the more stones remain for Bob to hoard.
- This realization will make implementing bruteforce much more simple, and from that, we can apply memoization for the top down approach. The implementation will becomes:
- If its Alice's turns, find max(alice_stone)
- If its Bob's turns, find min(alice_stone)
- We need to repeat the process for every possible M, we can do a simple for loop from 1 to M*2, remember to cover out of bound
- The state transition depends on 3 elements:
- Is it currently Alice's turns? (is_alice)
- Position of i and M (index, M)
Complexity
- Time complexity: $O(N^3)$. At worst case, M can go up to N in size
(N = piles.size()), at every index we need to loop 2*M times at worst and there are N work at every state.
- Space complexity: $O(N^2)$. N recursive call stack and N^2 for memoi table
Solution
Code
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n = piles.size();
if(n == 1) return piles[0];
vector<vector<vector<int>>> memo(n, vector<vector<int>>(n, vector<int>(2, -1)));
return helper(piles, 1, 0, true, memo);
}
int helper(vector<int>& piles, int M, int index, bool is_alice, vector<vector<vector<int>>>& memo) {
if (index >= piles.size()) return 0;
if (memo[index][M][is_alice] != -1) return memo[index][M][is_alice];
int maxStones = is_alice ? 0 : INT_MAX, currentStones = 0;
for (int i = 1; i <= 2 * M && index + i <= piles.size(); i++) {
currentStones += piles[index + i - 1];
if (is_alice) {
maxStones = max(maxStones, currentStones + helper(piles, max(M, i), index + i, false, memo));
} else {
maxStones = min(maxStones, helper(piles, max(M, i), index + i, true, memo));
}
}
memo[index][M][is_alice] = maxStones;
return maxStones;
}
};