Two players X and Y are playing a game in which there are pots of gold arranged in a line, each containing some gold coins. They get alternating turns in which the player can pick a pot from one of the ends of the line. The winner is the player who has a higher number of coins at the end. The objective is to maximize the number of coins collected by X, assuming Y also plays optimally. Return the maximum coins X could get while playing the game. Initially, X starts the game.
Examples:
Input: N = 4, Q[] = {8, 15, 3, 7}
Output: 22
Explanation: Player X starts and picks 7. Player Y picks the pot containing 8. Player X picks the pot containing 15. Player Y picks 3. Total coins collected by X = 7 + 15 = 22.Input:N = 4, A[] = {2, 2, 2, 2}
Output: 4
Approach: This can be solved with the following idea:
Using Dynamic programming as it has an optimum substructure and overlapping subproblems.
Steps involved in the implementation of code:
- Declare a static 1002 x 1002 2D integer array with the name dp.
- Add a public static method called “getMaxCoins” that accepts an integer array called “coins, ” together with the parameters “integer start” and “integer end, ” and returns an integer result.
- Return zero if start exceeds finish.
- Return dp[start][end] if dp[start][end] is not equal to -1.
- Create the variables option1 and option2 as two integers.
- GetMaxCoins with parameters coins, start+1, end-1, and getMaxCoins with parameters coins, start, end-2 are called recursively with the option1 set to coins[end] plus the minimum value in between.
- Option 2 should be set to coins[start] plus the minimum value in between calls to the recursive functions getMaxCoins with the arguments coins, start+2, end, and getMaxCoins with the arguments coins, start+1, end-1.
-
Set the highest value possible between options 1 and 2 for dp[start][end].
- Bring back dp[start][end].
- Make a public static method called maxCoins that receives an integer value as a return parameter and accepts integer array coins and an integer n as input arguments.
- To add -1 values to the dp array, use a nested loop.
- Return the value of the getMaxCoins method with parameters coins, 0, n-1
Below is the code implementation of the above approach:
// C++ code of the above approach #include <bits/stdc++.h> using namespace std;
int dp[1002][1002];
// Returns the maximum coins that // can be collected from start to end int getMaxCoins( int coins[], int start, int end)
{ // Base case: When start index is
// greater than end index
if (start > end) {
return 0;
}
// If we have already computed the
// solution for this sub-problem
if (dp[start][end] != -1) {
return dp[start][end];
}
// Case 1: Choose the last coin,
// then we can choose between the
// first and second-last coins
int option1
= coins[end]
+ min(getMaxCoins(coins, start + 1, end - 1),
getMaxCoins(coins, start, end - 2));
// Case 2: Choose the first coin,
// then we can choose between the
// second and last coins
int option2
= coins[start]
+ min(getMaxCoins(coins, start + 2, end),
getMaxCoins(coins, start + 1, end - 1));
// Store the maximum coins that
// can be collected from start
// to end
dp[start][end] = max(option1, option2);
return dp[start][end];
} int maxCoins( int coins[], int n)
{ // Initialize the DP array with -1
memset (dp, -1, sizeof (dp));
return getMaxCoins(coins, 0, n - 1);
} // Driver code int main()
{ int coins[] = { 8, 15, 3, 7 };
int n = sizeof (coins) / sizeof (coins[0]);
int maxCoin = maxCoins(coins, n);
// Function call
cout << maxCoin << endl;
return 0;
} |
// Java code of the above approach import java.util.*;
class GfG {
public static int dp[][] = new int [ 1002 ][ 1002 ];
// Returns the maximum coins that
// can be collected from start to end
public static int getMaxCoins( int [] coins, int start,
int end)
{
// Base case: When start index is
// greater than end index
if (start > end) {
return 0 ;
}
// If we have already computed the
// solution for this sub-problem
if (dp[start][end] != - 1 ) {
return dp[start][end];
}
// Case 1: Choose the last coin,
// then we can choose between the
// first and second-last coins
int option1
= coins[end]
+ Math.min(
getMaxCoins(coins, start + 1 , end - 1 ),
getMaxCoins(coins, start, end - 2 ));
// Case 2: Choose the first coin,
// then we can choose between the
// second and last coins
int option2
= coins[start]
+ Math.min(
getMaxCoins(coins, start + 2 , end),
getMaxCoins(coins, start + 1 , end - 1 ));
// Store the maximum coins that
// can be collected from start
// to end
dp[start][end] = Math.max(option1, option2);
return dp[start][end];
}
public static int maxCoins( int [] coins, int n)
{
// Initialize the DP array with -1
for ( int i = 0 ; i < 1001 ; i++) {
Arrays.fill(dp[i], - 1 );
}
return getMaxCoins(coins, 0 , n - 1 );
}
// Driver code
public static void main(String args[])
{
int [] coins = { 8 , 15 , 3 , 7 };
int n = coins.length;
int maxCoins = maxCoins(coins, n);
// Function call
System.out.println(maxCoins);
}
} |
class GfG:
dp = [[ - 1 for i in range ( 1002 )] for j in range ( 1002 )]
# Returns the maximum coins that
# can be collected from start to end
def getMaxCoins( self , coins, start, end):
# Base case: When start index is
# greater than end index
if start > end:
return 0
# If we have already computed the
# solution for this sub-problem
if self .dp[start][end] ! = - 1 :
return self .dp[start][end]
# Case 1: Choose the last coin,
# then we can choose between the
# first and second-last coins
option1 = coins[end] + min ( self .getMaxCoins(coins, start + 1 , end - 1 ), self .getMaxCoins(coins, start, end - 2 ))
# Case 2: Choose the first coin,
# then we can choose between the
# second and last coins
option2 = coins[start] + min ( self .getMaxCoins(coins, start + 2 , end), self .getMaxCoins(coins, start + 1 , end - 1 ))
# Store the maximum coins that
# can be collected from start
# to end
self .dp[start][end] = max (option1, option2)
return self .dp[start][end]
def maxCoins( self , coins, n):
# Initialize the DP array with -1
for i in range ( 1001 ):
for j in range ( 1001 ):
self .dp[i][j] = - 1
return self .getMaxCoins(coins, 0 , n - 1 )
# Driver code if __name__ = = '__main__' :
coins = [ 8 , 15 , 3 , 7 ]
n = len (coins)
gfg = GfG()
maxCoins = gfg.maxCoins(coins, n)
# Function call
print (maxCoins)
|
using System;
class GFG {
public static int [,] dp = new int [1002, 1002];
// Returns the maximum coins that can be collected from start to end
public static int getMaxCoins( int [] coins, int start, int end) {
// Base case: When start index is greater than end index
if (start > end) {
return 0;
}
// If we have already computed the solution for this sub-problem
if (dp[start, end] != -1) {
return dp[start, end];
}
// Case 1: Choose the last coin, then we can choose
// between the first and second-last coins
int option1 =
coins[end] + Math.Min(getMaxCoins(coins, start + 1, end - 1),
getMaxCoins(coins, start, end - 2));
// Case 2: Choose the first coin,
// then we can choose between the second and last coins
int option2 =
coins[start] + Math.Min(getMaxCoins(coins, start + 2, end),
getMaxCoins(coins, start + 1, end - 1));
// Store the maximum coins that can be collected from start to end
dp[start, end] = Math.Max(option1, option2);
return dp[start, end];
}
public static int maxCoins( int [] coins, int n) {
// Initialize the DP array with -1
for ( int i = 0; i < 1001; i++) {
for ( int j = 0; j < 1001; j++) {
dp[i,j] = -1;
}
}
return getMaxCoins(coins, 0, n - 1);
}
// Driver code
public static void Main() {
int [] coins = {8, 15, 3, 7};
int n = coins.Length;
int mxCoins = maxCoins(coins, n);
// Function call
Console.WriteLine(mxCoins);
}
} |
// JavaScript code of the above approach // Returns the maximum coins that // can be collected from start to end function getMaxCoins(coins, start, end, dp) {
// Base case: When start index is
// greater than end index
if (start > end) {
return 0;
}
// If we have already computed the
// solution for this sub-problem
if (dp[start][end] != -1) {
return dp[start][end];
}
// Case 1: Choose the last coin,
// then we can choose between the
// first and second-last coins
let option1 =
coins[end]
+ Math.min(getMaxCoins(coins, start + 1, end - 1, dp),
getMaxCoins(coins, start, end - 2, dp));
// Case 2: Choose the first coin,
// then we can choose between the
// second and last coins
let option2 =
coins[start]
+ Math.min(getMaxCoins(coins, start + 2, end, dp),
getMaxCoins(coins, start + 1, end - 1, dp));
// Store the maximum coins that
// can be collected from start
// to end
dp[start][end] = Math.max(option1, option2);
return dp[start][end];
} function maxCoins(coins, n) {
let dp = [];
// Initialize the DP array with -1
for (let i = 0; i <= n; i++) {
dp[i] = Array(n).fill(-1);
}
return getMaxCoins(coins, 0, n - 1, dp);
} // Driver code let coins = [ 8, 15, 3, 7 ]; let n = coins.length; let maxCoin = maxCoins(coins, n); // Function call console.log(maxCoin); // This code is contributed by prasad264 |
22
Time Complexity: O(N2)
Auxiliary Space: O(N2)
Efficient approach : Using DP Tabulation method ( Iterative approach )
The approach to solve this problem is same but DP tabulation(bottom-up) method is better then Dp + memoization(top-down) because memoization method needs extra stack space of recursion calls.
Steps to solve this problem :
- Create a DP of size n*n to store the solution of the subproblems .
- Now Iterate over subproblems to get the value of current problem form previous computation of subproblems stored in DP
- Return the final solution stored in dp[0][n – 1].
Implementation :
// C++ code of the above approach #include <bits/stdc++.h> using namespace std;
// Returns the maximum coins that // can be collected from start to end int maxCoins( int coins[], int n) {
// Base case
if (n == 0)
return 0;
//intialize dp
int dp[n][n];
//iterate over subproblems
for ( int len = 1; len <= n; len++) {
for ( int start = 0; start <= n - len; start++) {
int end = start + len - 1;
int x = ((start + 2 <= end) ? dp[start + 2][end] : 0);
int y = ((start + 1 <= end - 1) ? dp[start + 1][end - 1] : 0);
int z = ((start <= end - 2) ? dp[start][end - 2] : 0);
//current value
dp[start][end] = max(coins[start] + min(x, y), coins[end] + min(y, z));
}
}
//return answer
return dp[0][n - 1];
} //Driver Code int main() {
int coins[] = { 8, 15, 3, 7 };
int n = sizeof (coins) / sizeof (coins[0]);
int maxCoin = maxCoins(coins, n);
cout << maxCoin << endl;
return 0;
} |
public class MaxCoinsCollection {
// Returns the maximum coins that can be collected from start to end
public static int maxCoins( int [] coins) {
int n = coins.length;
// Initialize a 2D array to store the maximum coins
int [][] dp = new int [n][n];
// Iterate over subproblems
for ( int len = 1 ; len <= n; len++) {
for ( int start = 0 ; start <= n - len; start++) {
int end = start + len - 1 ;
int x = (start + 2 <= end) ? dp[start + 2 ][end] : 0 ;
int y = (start + 1 <= end - 1 ) ? dp[start + 1 ][end - 1 ] : 0 ;
int z = (start <= end - 2 ) ? dp[start][end - 2 ] : 0 ;
// Calculate the current value
dp[start][end] = Math.max(coins[start] + Math.min(x, y), coins[end] + Math.min(y, z));
}
}
// Return the answer
return dp[ 0 ][n - 1 ];
}
// Driver Code
public static void main(String[] args) {
int [] coins = { 8 , 15 , 3 , 7 };
int maxCoin = maxCoins(coins);
System.out.println(maxCoin);
}
} |
# Python code of the above approach def maxCoins(coins, n):
# Base case
if n = = 0 :
return 0
# intialize dp
dp = [[ 0 for j in range (n)] for i in range (n)]
# iterate over subproblems
for length in range ( 1 , n + 1 ):
for start in range (n - length + 1 ):
end = start + length - 1
x = dp[start + 2 ][end] if start + 2 < = end else 0
y = dp[start + 1 ][end - 1 ] if start + 1 < = end - 1 else 0
z = dp[start][end - 2 ] if start < = end - 2 else 0
# current value
dp[start][end] = max (coins[start] + min (x, y), coins[end] + min (y, z))
# return answer
return dp[ 0 ][n - 1 ]
# Driver Code coins = [ 8 , 15 , 3 , 7 ]
n = len (coins)
maxCoin = maxCoins(coins, n)
print (maxCoin)
# This code is contributed by Sakshi |
using System;
class Program
{ // Returns the maximum coins that can be collected from start to end
static int MaxCoins( int [] coins, int n)
{
// Base case
if (n == 0)
return 0;
// Initialize dp
int [,] dp = new int [n, n];
// Iterate over subproblems
for ( int len = 1; len <= n; len++)
{
for ( int start = 0; start <= n - len; start++)
{
int end = start + len - 1;
int x = (start + 2 <= end) ? dp[start + 2, end] : 0;
int y = (start + 1 <= end - 1) ? dp[start + 1, end - 1] : 0;
int z = (start <= end - 2) ? dp[start, end - 2] : 0;
// Current value
dp[start, end] = Math.Max(coins[start] + Math.Min(x, y), coins[end] + Math.Min(y, z));
}
}
// Return answer
return dp[0, n - 1];
}
// Driver Code
static void Main( string [] args)
{
int [] coins = { 8, 15, 3, 7 };
int n = coins.Length;
int maxCoin = MaxCoins(coins, n);
Console.WriteLine(maxCoin);
}
} |
function maxCoins(coins) {
const n = coins.length;
// Base case
if (n === 0) {
return 0;
}
// Initialize a 2D array for dynamic programming
const dp = new Array(n).fill(0).map(() => new Array(n).fill(0));
// Iterate over subproblems
for (let len = 1; len <= n; len++) {
for (let start = 0; start <= n - len; start++) {
const end = start + len - 1;
const x = start + 2 <= end ? dp[start + 2][end] : 0;
const y = start + 1 <= end - 1 ? dp[start + 1][end - 1] : 0;
const z = start <= end - 2 ? dp[start][end - 2] : 0;
// Current value
dp[start][end] = Math.max(coins[start] + Math.min(x, y), coins[end] + Math.min(y, z));
}
}
// Return the answer
return dp[0][n - 1];
} // Driver Code const coins = [8, 15, 3, 7]; const maxCoin = maxCoins(coins); console.log(maxCoin); |
Output
22
Time Complexity: O(N^2)
Auxiliary Space: O(N^2)