Find the winner of a game of removing at most 3 stones from a pile in each turn
Given an array arr[] of size N, denoting values assigned to N stones, two players, Player1 and Player2, play a game of alternating turns. In each turn, a player can take 1, 2, or 3 stones from the first remaining stones with the sum of values of all the removed stones added to the player’s score. Considering that both players play optimally, the task is to print the winner of the game. If both players end the game with the same score, print “Tie”.
Examples:
Input: arr[] = {1, 2, 3, 7}
Output: Player2
Explanation: Player1 will always lose in an optimal scenario.
Input: arr[] = {1, 2, 3, -9}
Output: Player1
Explanation: Player1 must choose all the three piles at the first move to win and leave Player2 with negative score.
If Player1 chooses only one stone his score will be 1 and the next move Player2 score becomes 5.
The next move Player1 will take the stone with value = -9 and lose.
If Player1 chooses two piles his score will be 3 and the next move Player2 score becomes 3.
The next move Player1 will take the stone with value = -9 and also lose.
Naive Approach: The simple approach is to pick the number of stones that will maximize the total sum of the stone’s values. As both players play optimally and Player1 starts the game, Player1 picks either 1 or 2 or 3 stones and the remaining stones passed to the next player. Therefore, the score of Player2 must be subtracted from score of Player1. The idea is to use recursion to solve the problem. Let the maximum score of Player1 be res which is obtained after the recursive calls.
- If the result, res > 0, Player1 wins
- If the result, res < 0, Player2 wins
- If the result, res == 0, then it is a tie.
The recursive tree looks like this, where some subproblems are repeated many times.
Follow the steps to solve the problem:
- Declare a recursive function, say maxScore(i), to calculate the maximum score of Player1 if the game starts at index i
- If the value of i ≥ n, return 0.
- Initialize a variable, say score as INT_MIN, to store the maximum score of Player1
- Picks 1 stone: score = max(score, arr[i] – maxScore(i + 1))
- Picks 2 stones i.e (i + 1 < N): score = max(score, arr[i] + arr[i + 1] – maxScore(i + 2))
- Picks 3 stones i.e (i + 2 < N): score = max(score, arr[i] + arr[i + 1] + arr[i + 2] – maxScore(i + 3))
- Return the value of the score.
- Store the value of maxScore(0) in a variable res.
- If the value of res>0, print “Player1”, if res<0, print “Player2”, otherwise print “Tie”.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
int maximumStonesUtil( int * arr, int n, int i)
{
if (i >= n)
return 0;
int ans = INT_MIN;
ans = max(ans,
arr[i] - maximumStonesUtil(arr, n, i + 1));
if (i + 1 < n)
ans = max(ans,
arr[i] + arr[i + 1]
- maximumStonesUtil(arr, n, i + 2));
if (i + 2 < n)
ans = max(ans,
arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n, i + 3));
return ans;
}
string maximumStones( int * arr, int n)
{
int res = maximumStonesUtil(arr, n, 0);
if (res > 0)
return "Player1" ;
else if (res < 0)
return "Player2" ;
else
return "Tie" ;
}
int main()
{
int arr[] = { 1, 2, 3, 7 };
int n = sizeof (arr) / sizeof (arr[0]);
cout << maximumStones(arr, n);
return 0;
}
|
Java
class GFG{
static int maximumStonesUtil( int [] arr, int n,
int i)
{
if (i >= n)
return 0 ;
int ans = Integer.MIN_VALUE;
ans = Math.max(ans, arr[i] - maximumStonesUtil(
arr, n, i + 1 ));
if (i + 1 < n)
ans = Math.max(ans, arr[i] + arr[i + 1 ] -
maximumStonesUtil(arr, n, i + 2 ));
if (i + 2 < n)
ans = Math.max(
ans,
arr[i] + arr[i + 1 ] + arr[i + 2 ]
- maximumStonesUtil(arr, n, i + 3 ));
return ans;
}
static String maximumStones( int [] arr, int n)
{
int res = maximumStonesUtil(arr, n, 0 );
if (res > 0 )
return "Player1" ;
else if (res < 0 )
return "Player2" ;
else
return "Tie" ;
}
public static void main(String[] args)
{
int arr[] = { 1 , 2 , 3 , 7 };
int n = arr.length;
System.out.println(maximumStones(arr, n));
}
}
|
Python3
import sys
def maximumStonesUtil(arr, n, i):
if (i > = n):
return 0
ans = - sys.maxsize - 1 ;
ans = max (
ans, arr[i] - maximumStonesUtil(
arr, n, i + 1 ))
if (i + 1 < n):
ans = max (
ans, arr[i] + arr[i + 1 ] - maximumStonesUtil(
arr, n, i + 2 ))
if (i + 2 < n):
ans = max (
ans, arr[i] + arr[i + 1 ] + arr[i + 2 ] -
maximumStonesUtil(arr, n, i + 3 ));
return ans
def maximumStones(arr, n):
res = maximumStonesUtil(arr, n, 0 )
if (res > 0 ):
return "Player1"
elif (res < 0 ):
return "Player2"
else :
return "Tie"
if __name__ = = '__main__' :
arr = [ 1 , 2 , 3 , 7 ]
n = len (arr)
print (maximumStones(arr, n))
|
C#
using System;
class GFG
{
static int maximumStonesUtil( int [] arr, int n,
int i)
{
if (i >= n)
return 0;
int ans = Int32.MinValue;
ans = Math.Max(ans, arr[i] - maximumStonesUtil(
arr, n, i + 1));
if (i + 1 < n)
ans = Math.Max(ans, arr[i] + arr[i + 1] -
maximumStonesUtil(arr, n, i + 2));
if (i + 2 < n)
ans = Math.Max(
ans,
arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n, i + 3));
return ans;
}
static String maximumStones( int [] arr, int n)
{
int res = maximumStonesUtil(arr, n, 0);
if (res > 0)
return "Player1" ;
else if (res < 0)
return "Player2" ;
else
return "Tie" ;
}
public static void Main()
{
int [] arr = { 1, 2, 3, 7 };
int n = arr.Length;
Console.WriteLine(maximumStones(arr, n));
}
}
|
Javascript
<script>
function maximumStonesUtil(arr, n, i)
{
if (i >= n)
return 0;
let ans = Number.MIN_VALUE;
ans = Math.max(ans, arr[i] - maximumStonesUtil(arr, n, i + 1));
if (i + 1 < n)
ans = Math.max(ans, arr[i] + arr[i + 1] - maximumStonesUtil(arr, n, i + 2));
if (i + 2 < n)
ans = Math.max(
ans,
arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n, i + 3));
return ans;
}
function maximumStones(arr, n)
{
let res = maximumStonesUtil(arr, n, 0);
if (res < 0)
return "Player1" ;
else if (res > 0)
return "Player2" ;
else
return "Tie" ;
}
let arr = [ 1, 2, 3, 7 ];
let n = arr.length;
document.write(maximumStones(arr, n));
</script>
|
Time Complexity: O(3N)
Auxiliary Space: O(N)
Efficient Approach: To optimize the above approach, the idea is to use dynamic programming. The given problem has optimal substructure property and overlapping subproblems. Use memoization by creating a 1D table, dp of size N to store the results of the recursive calls.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
int maximumStonesUtil( int * arr, int n, int i,
vector< int >& dp)
{
if (i >= n)
return 0;
int & ans = dp[i];
if (ans != -1)
return ans;
ans = INT_MIN;
ans = max(
ans, arr[i] - maximumStonesUtil(arr, n, i + 1, dp));
if (i + 1 < n)
ans = max(ans, arr[i] + arr[i + 1]
- maximumStonesUtil(arr, n,
i + 2, dp));
if (i + 2 < n)
ans = max(ans, arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n,
i + 3, dp));
return ans;
}
string maximumStones( int * arr, int n)
{
vector< int > dp(n, -1);
int res = maximumStonesUtil(arr, n, 0, dp);
if (res > 0)
return "Player1" ;
else if (res < 0)
return "Player2" ;
else
return "Tie" ;
}
int main()
{
int arr[] = { 1, 2, 3, 7 };
int n = sizeof (arr) / sizeof (arr[0]);
cout << maximumStones(arr, n);
return 0;
}
|
Java
import java.util.*;
class GFG
{
static int ans = 0 ;
static int maximumStonesUtil( int [] arr, int n, int i,
int [] dp)
{
if (i >= n)
return 0 ;
ans = dp[i];
if (ans != - 1 )
return ans;
ans = Integer.MIN_VALUE;
ans = Math.max(
ans, arr[i] - maximumStonesUtil(arr, n, i + 1 , dp));
if (i + 1 < n)
ans = Math.max(ans, arr[i] + arr[i + 1 ]
- maximumStonesUtil(arr, n,
i + 2 , dp));
if (i + 2 < n)
ans = Math.max(ans, arr[i] + arr[i + 1 ] + arr[i + 2 ]
- maximumStonesUtil(arr, n,
i + 3 , dp));
return ans;
}
static String maximumStones( int []arr, int n)
{
int []dp = new int [n];
Arrays.fill(dp, - 1 );
int res = maximumStonesUtil(arr, n, 0 , dp);
if (res > 0 )
return "Player1" ;
else if (res < 0 )
return "Player2" ;
else
return "Tie" ;
}
public static void main(String[] args)
{
int arr[] = { 1 , 2 , 3 , 7 };
int n = arr.length;
System.out.print(maximumStones(arr, n));
}
}
|
Python3
def maximumStonesUtil(arr, n, i, dp):
if (i > = n):
return 0
ans = dp[i]
if (ans ! = - 1 ):
return ans
ans = - 2 * * 31
ans = max ( ans, arr[i] - maximumStonesUtil(arr, n, i + 1 , dp))
if (i + 1 < n):
ans = max (ans, arr[i] + arr[i + 1 ] - maximumStonesUtil(arr, n, i + 2 , dp))
if (i + 2 < n):
ans = max (ans, arr[i] + arr[i + 1 ] + arr[i + 2 ] - maximumStonesUtil(arr, n, i + 3 , dp))
return ans
def maximumStones(arr, n):
dp = [ - 1 ] * n
res = maximumStonesUtil(arr, n, 0 , dp)
if (res > 0 ):
return "Player1"
elif (res < 0 ):
return "Player2"
else :
return "Tie"
arr = [ 1 , 2 , 3 , 7 ]
n = len (arr)
print (maximumStones(arr, n))
|
C#
using System;
public class GFG
{
static int ans = 0;
static int maximumStonesUtil( int [] arr, int n, int i,
int [] dp)
{
if (i >= n)
return 0;
ans = dp[i];
if (ans != -1)
return ans;
ans = int .MinValue;
ans = Math.Max(
ans, arr[i] - maximumStonesUtil(arr, n, i + 1, dp));
if (i + 1 < n)
ans = Math.Max(ans, arr[i] + arr[i + 1]
- maximumStonesUtil(arr, n,
i + 2, dp));
if (i + 2 < n)
ans = Math.Max(ans, arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n,
i + 3, dp));
return ans;
}
static String maximumStones( int []arr, int n)
{
int []dp = new int [n];
for ( int i = 0; i < n; i++)
dp[i]=-1;
int res = maximumStonesUtil(arr, n, 0, dp);
if (res > 0)
return "Player1" ;
else if (res < 0)
return "Player2" ;
else
return "Tie" ;
}
public static void Main(String[] args)
{
int []arr = { 1, 2, 3, 7 };
int n = arr.Length;
Console.Write(maximumStones(arr, n));
}
}
|
Javascript
<script>
let ans = 0 ;
function maximumStonesUtil( arr, n, i,
dp)
{
if (i >= n)
return 0;
ans = dp[i];
if (ans != -1)
return ans;
ans = -Math.pow(2,31);
ans = Math.max(
ans, arr[i] - maximumStonesUtil(arr, n, i + 1, dp));
if (i + 1 < n)
ans = Math.max(ans, arr[i] + arr[i + 1]
- maximumStonesUtil(arr, n,
i + 2, dp));
if (i + 2 < n)
ans = Math.max(ans, arr[i] + arr[i + 1] + arr[i + 2]
- maximumStonesUtil(arr, n,
i + 3, dp));
return ans;
}
function maximumStones(arr, n)
{
let dp = new Array(n).fill(-1);
let res = maximumStonesUtil(arr, n, 0, dp);
if (res > 0)
return "Player1" ;
else if (res < 0)
return "Player2" ;
else
return "Tie" ;
}
let arr= [ 1, 2, 3, 7 ];
let n = arr.length;
document.write(maximumStones(arr, n));
<script>
|
Time Complexity: O(N)
Auxiliary Space: O(N)
Efficient Approach: Using the DP Tabulation method ( Iterative approach )
The approach to solving this problem is the same but DP tabulation(bottom-up) method is better than the Dp + memoization(top-down) because the memoization method needs extra stack space of recursion calls.
Steps to solve this problem:
- Create a DP of size n+3 to store the solution of the subproblems.
- Initialize the DP with base cases dp[n] = dp[n+1] = dp[n+2] = 0.
- Now Iterate over subproblems to get the value of the current problem from the previous computation of subproblems stored in DP
- Print the final result
- if dp[0] > 0 print player1.
- if dp[0] < 0 print player2.
- else print tie.
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
string maximumStones( int * arr, int n)
{
vector< int > dp(n + 3);
dp[n] = dp[n + 1] = dp[n + 2] = 0;
for ( int i = n - 1; i >= 0; i--) {
int a = arr[i] - dp[i + 1];
int b = -1 , c= -1;
if (i+1 == n){
b = arr[i]- dp[i + 2];
c = arr[i] - dp[i + 3];
}
else if ( i+2 == n){
c = arr[i] + arr[i + 1] - dp[i + 3];
}
if (b==-1)
b = arr[i] + arr[i + 1] - dp[i + 2];
if (c == -1)
c = arr[i] + arr[i + 1] + arr[i + 2] - dp[i + 3];
dp[i] = max(a,max(b,c));
}
if (dp[0] > 0)
return "Player1" ;
else if (dp[0] < 0)
return "Player2" ;
else
return "Tie" ;
}
int main()
{
int arr[] = { 1, 2, 3, 7 };
int n = sizeof (arr) / sizeof (arr[0]);
cout << maximumStones(arr, n);
return 0;
}
|
Java
import java.util.*;
public class Main {
public static String maximumStones( int [] arr, int n) {
int [] dp = new int [n + 3 ];
dp[n] = dp[n + 1 ] = dp[n + 2 ] = 0 ;
for ( int i = n - 1 ; i >= 0 ; i--) {
int a = arr[i] - dp[i + 1 ];
int b = - 1 , c = - 1 ;
if (i + 1 == n) {
b = arr[i] - dp[i + 2 ];
c = arr[i] - dp[i + 3 ];
} else if (i + 2 == n) {
c = arr[i] + arr[i + 1 ] - dp[i + 3 ];
}
if (b == - 1 )
b = arr[i] + arr[i + 1 ] - dp[i + 2 ];
if (c == - 1 )
c = arr[i] + arr[i + 1 ] + arr[i + 2 ] - dp[i + 3 ];
dp[i] = Math.max(a, Math.max(b, c));
}
if (dp[ 0 ] > 0 )
return "Player1" ;
else if (dp[ 0 ] < 0 )
return "Player2" ;
else
return "Tie" ;
}
public static void main(String[] args) {
int [] arr = { 1 , 2 , 3 , 7 };
int n = arr.length;
System.out.println(maximumStones(arr, n));
}
}
|
Python
def maximumStones(arr, n):
dp = [ 0 ] * (n + 3 )
dp[n] = dp[n + 1 ] = dp[n + 2 ] = 0
for i in range (n - 1 , - 1 , - 1 ):
a = arr[i] - dp[i + 1 ]
b = c = - 1
if i + 1 = = n:
b = arr[i] - dp[i + 2 ]
c = arr[i] - dp[i + 3 ]
elif i + 2 = = n:
c = arr[i] + arr[i + 1 ] - dp[i + 3 ]
if b = = - 1 :
b = arr[i] + arr[i + 1 ] - dp[i + 2 ]
if c = = - 1 :
c = arr[i] + arr[i + 1 ] + arr[i + 2 ] - dp[i + 3 ]
dp[i] = max (a, max (b, c))
if dp[ 0 ] > 0 :
return "Player1"
elif dp[ 0 ] < 0 :
return "Player2"
else :
return "Tie"
if __name__ = = '__main__' :
arr = [ 1 , 2 , 3 , 7 ]
n = len (arr)
print (maximumStones(arr, n))
|
C#
using System;
class GFG {
public static string MaximumStones( int [] arr, int n) {
int [] dp = new int [n + 3];
dp[n] = dp[n + 1] = dp[n + 2] = 0;
for ( int i = n - 1; i >= 0; i--) {
int a = arr[i] - dp[i + 1];
int b = -1, c = -1;
if (i + 1 == n) {
b = arr[i] - dp[i + 2];
c = arr[i] - dp[i + 3];
}
else if (i + 2 == n) {
c = arr[i] + arr[i + 1] - dp[i + 3];
}
if (b == -1)
b = arr[i] + arr[i + 1] - dp[i + 2];
if (c == -1)
c = arr[i] + arr[i + 1] + arr[i + 2] - dp[i + 3];
dp[i] = Math.Max(a, Math.Max(b, c));
}
if (dp[0] > 0)
return "Player1" ;
else if (dp[0] < 0)
return "Player2" ;
else
return "Tie" ;
}
public static void Main( string [] args) {
int [] arr = { 1, 2, 3, 7 };
int n = arr.Length;
Console.WriteLine(MaximumStones(arr, n));
}
}
|
Javascript
function maximumStones(arr) {
let n = arr.length;
let dp = Array(n + 3).fill(0);
dp[n] = dp[n + 1] = dp[n + 2] = 0;
for (let i = n - 1; i >= 0; i--) {
let a = arr[i] - dp[i + 1];
let b = c = -1;
if (i + 1 === n) {
b = arr[i] - dp[i + 2];
c = arr[i] - dp[i + 3];
} else if (i + 2 === n) {
c = arr[i] + arr[i + 1] - dp[i + 3];
}
if (b === -1) {
b = arr[i] + arr[i + 1] - dp[i + 2];
}
if (c === -1) {
c = arr[i] + arr[i + 1] + arr[i + 2] - dp[i + 3];
}
dp[i] = Math.max(a, Math.max(b, c));
}
if (dp[0] > 0) {
return "Player1" ;
}
else if (dp[0] < 0) {
return "Player2" ;
}
else {
return "Tie" ;
}
}
let arr = [1, 2, 3, 7];
console.log(maximumStones(arr));
|
Time Complexity: O(N)
Auxiliary Space: O(N)
Last Updated :
25 Jul, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...