Find the maximum gain in a two-players game where an array is given and each player in turn is allowed to pick one element from either end of the array. The goal of each player is to score more than his opponent. Does the player who plays first always win?
OR
Given an unsorted array of pots containing coins.
A and B play a game where they take turns and pick up a pot either from the beginning or the end of array.
If A plays first, devise a strategy to help him win the game.
A greedy approach would not work since picking up 2 would leave the 100 element at one of the ends to be picked up by our opponent. A better strategy is to think in terms of subproblems
publicclassSolution {
publicintmaxGainRecursive(int[] nums) {
return helper(nums, 0, nums.length- 1);
}
privateinthelper(int[] nums, int i, int j) {
if (i > j)
return 0;
int sumIJ = sum(nums, i, j);
return Math.max(nums[i]+ sumIJ - nums[i]- helper(nums, i + 1, j),
nums[j]+ sumIJ - nums[j]- helper(nums, i, j - 1));
}
privateintsum(int[] nums, int i, int j) {
int sum = 0;
for (int k = i; k <= j; k++) sum += nums[k];
return sum;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
defmax_gain_recursive(nums):
defhelper(i, j):
if i > j:
return0return max(
nums[i] + sum(nums[i +1 : j +1]) - helper(i +1, j),
nums[j] + sum(nums[i:j]) - helper(i, j -1),
)
return helper(0, len(nums) -1)
# Example usagenums = [1, 5, 3, 7, 8, 2]
print(max_gain_recursive(nums)) # Output: Maximum gain
#### Complexity
- ⏰ Time complexity: `O(2^n)`, because each call splits into two further calls, leading to an exponential number of calls.
- 🧺 Space complexity: `O(n)`
### Method 2 - Top Down DP
#### Code
publicclassSolution {
publicintmaxGainTopDown(int[] nums) {
int n = nums.length;
Integer[][] memo =new Integer[n][n];
return helper(nums, 0, n - 1, memo);
}
privateinthelper(int[] nums, int i, int j, Integer[][] memo) {
if (i > j)
return 0;
if (memo[i][j]!=null)
return memo[i][j];
int sumIJ = sum(nums, i, j);
memo[i][j]= Math.max(nums[i]+ sumIJ - nums[i]- helper(nums, i + 1, j, memo),
nums[j]+ sumIJ - nums[j]- helper(nums, i, j - 1, memo));
return memo[i][j];
}
privateintsum(int[] nums, int i, int j) {
int sum = 0;
for (int k = i; k <= j; k++) sum += nums[k];
return sum;
}
publicstaticvoidmain(String[] args) {
Solution solution =new Solution();
int[] nums = {1, 5, 3, 7, 8, 2};
System.out.println(
solution.maxGainTopDown(nums)); // Output: Maximum gain }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defmax_gain_top_down(nums):
n = len(nums)
memo = [[None] * n for _ in range(n)]
defhelper(i, j):
if i > j:
return0if memo[i][j] isnotNone:
return memo[i][j]
sumIJ = sum(nums[i : j +1])
memo[i][j] = max(
nums[i] + sumIJ - nums[i] - helper(i +1, j),
nums[j] + sumIJ - nums[j] - helper(i, j -1),
)
return memo[i][j]
return helper(0, len(nums) -1)
#### Complexity
- ⏰ Time complexity: `O(n^2)`, because we fill an `n x n` table with results for subproblems.
- 🧺 Space complexity: `O(n^2)`, due to the memoization table that stores results of overlapping subproblems.
### Method 3 - Bottom up DP
#### Code
defmax_gain_bottom_up(nums):
n = len(nums)
dp = [[0] * n for _ in range(n)]
sum_arr = [0] * (n +1)
for i in range(n):
sum_arr[i +1] = sum_arr[i] + nums[i]
for length in range(1, n +1):
for i in range(n - length +1):
j = i + length -1 sumIJ = sum_arr[j +1] - sum_arr[i]
if i == j:
dp[i][j] = nums[i]
else:
dp[i][j] = max(
nums[i] + sumIJ - nums[i] - dp[i +1][j],
nums[j] + sumIJ - nums[j] - dp[i][j -1],
)
return dp[0][n -1]
#### Complexity
- ⏰ Time complexity: `O(n^2)`, because we fill an `n x n` DP table.
- 🧺 Space complexity: `O(n^2)`, due to the DP table and the additional space for prefix sums.