Given an array arr[], where each element represents a pile of stone, two players Player1 and Player2 play a game taking alternating turns. In each turn, a player may take 1, 2, or 3 consecutive piles of stones from the beginning of the remaining array which will be added to there respective score. Considering that both players play optimally, we have to find the result of the game, Player with the highest score.
- Return 1 -> if player 1 wins the game.
- Return 2 -> if player 2 wins the game.
- Return 0 -> if game ends as a tie.
Examples:
Input: arr[] = [1, 2, 3, 7]
Output: 2
Explanation: Player1 will always lose as:
In any scenario, even if player1 choose (1), (1+2) or (1+2+3) as his score
player2 will win by choosing (2+3+7), (3+7) or (7).Input: arr[] = [4, 2, 3, 7]
Output: 1
Explanation: Player2 will lose as:
player1 can pick (4), (4+2) or (4+2+3) as his score on his first move.
then player2 can pick (2+3+7), (3+7) or (7).
so (4+2+3), picking the first 3 piles of stone is ideal move of player1.
Table of Content
[Naive Approach]: Using basic Recursion
The idea is to use recursion to try out every possible move where a player can pick 1, 2, or 3 piles of stones from the start that are unpicked. At each step, the player chooses the option that gives the best overall score, keeping in mind that the opponent will also play optimally in the next turn. Here we have to calculates the best score difference that the current players can achieve from a given position. In the end finally, based on whether the difference is positive, negative, or zero, we will determine the winner of the game.
Follow the steps to solve the problem:
- Declare a recursive function, that will 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.
- return the result according to res.
#include <iostream>
#include <climits>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
// Function to find the maximum score of Player1
int maximumStonesUtil(vector<int>& arr, int k)
{
int n = arr.size();
// Base Case
if (k >= n)
return 0;
// Variable to store maximum score
int ans = INT_MIN;
// Pick one stone
ans = max(ans,
arr[k] - maximumStonesUtil(arr, k + 1));
// Pick 2 stones
if (k + 1 < n)
ans = max(ans,
arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2));
// Pick 3 stones
if (k + 2 < n)
ans = max(ans,
arr[k] + arr[k + 1] + arr[k + 2]
- maximumStonesUtil(arr, k + 3));
// Return the score of the player
return ans;
}
// Function to find the winner of the game
int maximumStones(vector<int>& arr)
{
int n = arr.size();
// Store the result
int res = maximumStonesUtil(arr, 0);
// Determine the winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
int main()
{
// Given Input
vector<int> arr = { 1, 2, 3, 7 };
// Function Call
cout << maximumStones(arr);
return 0;
}
public class Main {
// Function to find the maximum score of Player1
static int maximumStonesUtil(int[] arr, int k) {
int n = arr.length;
// Base Case
if (k >= n)
return 0;
// Variable to store maximum score
int ans = Integer.MIN_VALUE;
// Pick one stone
ans = Math.max(ans, arr[k] - maximumStonesUtil(arr, k + 1));
// Pick 2 stones
if (k + 1 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2));
// Pick 3 stones
if (k + 2 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - maximumStonesUtil(arr, k + 3));
// Return the score of the player
return ans;
}
// Function to find the winner of the game
static int maximumStones(int[] arr) {
int res = maximumStonesUtil(arr, 0);
// Determine the winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
public static void main(String[] args) {
// Given Input
int[] arr = {1, 2, 3, 7};
// Function Call
System.out.println(maximumStones(arr));
}
}
# Function to find the maximum score of Player1
def maximumStonesUtil(arr, k):
n = len(arr)
# Base Case
if k >= n:
return 0
# Variable to store maximum score
ans = float('-inf')
# Pick one stone
ans = max(ans, arr[k] - maximumStonesUtil(arr, k + 1))
# Pick 2 stones
if k + 1 < n:
ans = max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2))
# Pick 3 stones
if k + 2 < n:
ans = max(ans, arr[k] + arr[k + 1] + arr[k + 2]
- maximumStonesUtil(arr, k + 3))
# Return the score of the player
return ans
# Function to find the winner of the game
def maximumStones(arr):
# Store the result
res = maximumStonesUtil(arr, 0)
# Determine the winner
if res > 0:
return 1
elif res < 0:
return 2
else:
return 0
# Driver Code
if __name__ == "__main__":
# Given Input
arr = [1, 2, 3, 7]
# Function Call
print(maximumStones(arr))
using System;
class Program
{
// Function to find the maximum score of Player1
static int MaximumStonesUtil(int[] arr, int k)
{
int n = arr.Length;
// Base Case
if (k >= n)
return 0;
// Variable to store maximum score
int ans = int.MinValue;
// Pick one stone
ans = Math.Max(ans, arr[k] - MaximumStonesUtil(arr, k + 1));
// Pick 2 stones
if (k + 1 < n)
ans = Math.Max(ans, arr[k] + arr[k + 1]
- MaximumStonesUtil(arr, k + 2));
// Pick 3 stones
if (k + 2 < n)
ans = Math.Max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - MaximumStonesUtil(arr, k + 3));
// Return the score of the player
return ans;
}
// Function to find the winner of the game
static int MaximumStones(int[] arr)
{
int res = MaximumStonesUtil(arr, 0);
// Determine the winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
static void Main()
{
// Given Input
int[] arr = { 1, 2, 3, 7 };
// Function Call
Console.WriteLine(MaximumStones(arr));
}
}
// Function to find the maximum score of Player1
function maximumStonesUtil(arr, k) {
const n = arr.length;
// Base Case
if (k >= n)
return 0;
// Variable to store maximum score
let ans = Number.NEGATIVE_INFINITY;
// Pick one stone
ans = Math.max(ans, arr[k] - maximumStonesUtil(arr, k + 1));
// Pick 2 stones
if (k + 1 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2));
// Pick 3 stones
if (k + 2 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - maximumStonesUtil(arr, k + 3));
// Return the score of the player
return ans;
}
// Function to find the winner of the game
function maximumStones(arr) {
const res = maximumStonesUtil(arr, 0);
// Determine the winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
const arr = [1, 2, 3, 7];
// Function Call
console.log(maximumStones(arr));
Output
2
Time Complexity: O(3n)
Auxiliary Space: O(n)
[Efficient Approach I]: Using dynamic programming ( top-down approach )
To optimize the above approach, we will be using Dynamic Programming , this problem exhibits both the optimal substructure and overlapping subproblems properties, making it a perfect fit for DP. We will use memoization by creating a
dpto store the results of previously computed recursive calls, which helps eliminate redundant calculations and significantly improve efficiency.
#include <iostream>
#include <vector>
#include <climits>
#include <string>
#include <algorithm>
using namespace std;
// Function to find the maximum score of Player 1
int maximumStonesUtil(vector<int>& arr, int k, vector<int>& dp)
{
int n = arr.size();
// Base Case
if (k >= n)
return 0;
// If the result is already computed, return it
if (dp[k] != -1)
return dp[k];
// Variable to store maximum score
int ans = INT_MIN;
// Pick 1 stone
ans = max(ans, arr[k] - maximumStonesUtil(arr, k + 1, dp));
// Pick 2 stones
if (k + 1 < n)
ans = max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2, dp));
// Pick 3 stones
if (k + 2 < n)
ans = max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - maximumStonesUtil(arr, k + 3, dp));
// Store result in dp and return
dp[k] = ans;
return dp[k];
}
// Function to find the winner of the game
int maximumStones(vector<int>& arr)
{
int n = arr.size();
// Create a 1D DP table
vector<int> dp(n, -1);
// Store the result
int res = maximumStonesUtil(arr, 0, dp);
// Determine winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
int main()
{
vector<int> arr = {1, 2, 3, 7};
cout << maximumStones(arr);
return 0;
}
import java.util.*;
public class Main {
// Function to find the maximum score of Player 1
static int maximumStonesUtil(int[] arr, int k, int[] dp) {
int n = arr.length;
// Base Case
if (k >= n)
return 0;
// If the result is already computed, return it
if (dp[k] != -1)
return dp[k];
// Variable to store maximum score
int ans = Integer.MIN_VALUE;
// Pick 1 stone
ans = Math.max(ans, arr[k] - maximumStonesUtil(arr, k + 1, dp));
// Pick 2 stones
if (k + 1 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2, dp));
// Pick 3 stones
if (k + 2 < n)
ans = Math.max(ans, arr[k] + arr[k + 1] + arr[k + 2]
- maximumStonesUtil(arr, k + 3, dp));
// Store result in dp and return
dp[k] = ans;
return dp[k];
}
// Function to find the winner of the game
static int maximumStones(int[] arr) {
int n = arr.length;
// Create a 1D DP table
int[] dp = new int[n];
Arrays.fill(dp, -1);
// Store the result
int res = maximumStonesUtil(arr, 0, dp);
// Determine the winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
public static void main(String[] args) {
int[] arr = {1, 2, 3, 7};
System.out.println(maximumStones(arr));
}
}
def maximumStonesUtil(arr, k, dp):
n = len(arr)
# Base Case
if (k >= n):
return 0
ans = dp[k]
# If the result is already computed, then
# return the result
if (ans != -1):
return ans
# Variable to store maximum score
ans = -2**31
# Pick one stone
ans = max( ans, arr[k] - maximumStonesUtil(arr, k + 1, dp))
# Pick 2 stones
if (k + 1 < n):
ans = max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2, dp))
# Pick 3 stones
if (k + 2 < n):
ans = max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - maximumStonesUtil(arr, k + 3, dp))
# Return the score of the player
return ans
# Function to find the winner of the game
def maximumStones(arr):
n = len(arr)
# Create a 1D table, dp of size N
dp = [-1] * n
# Store the result
res = maximumStonesUtil(arr, 0, dp)
# Determine winner
if (res > 0):
return 1
elif (res < 0):
return 2
else:
return 0
# Driver Code
# Given Input
arr = [1, 2, 3, 7]
n = len(arr)
# Function Call
print(maximumStones(arr))
# This code is contributed by shivani
using System;
class Program
{
// Function to find the maximum score of Player 1
static int MaximumStonesUtil(int[] arr, int k, int[] dp)
{
int n = arr.Length;
// Base Case
if (k >= n)
return 0;
// If the result is already computed, return it
if (dp[k] != -1)
return dp[k];
// Variable to store maximum score
int ans = int.MinValue;
// Pick 1 stone
ans = Math.Max(ans, arr[k] - MaximumStonesUtil(arr, k + 1, dp));
// Pick 2 stones
if (k + 1 < n)
ans = Math.Max(ans, arr[k] + arr[k + 1]
- MaximumStonesUtil(arr, k + 2, dp));
// Pick 3 stones
if (k + 2 < n)
ans = Math.Max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - MaximumStonesUtil(arr, k + 3, dp));
// Store result in dp and return
dp[k] = ans;
return dp[k];
}
// Function to find the winner of the game
static int MaximumStones(int[] arr)
{
int n = arr.Length;
// Create a 1D DP table
int[] dp = new int[n];
for (int k = 0; k < n; k++)
dp[k] = -1;
// Store the result
int res = MaximumStonesUtil(arr, 0, dp);
// Determine winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
static void Main()
{
int[] arr = { 1, 2, 3, 7 };
Console.WriteLine(MaximumStones(arr));
}
}
// Function to find the maximum score of Player 1
function maximumStonesUtil(arr, k, dp) {
const n = arr.length;
// Base Case
if (k >= n)
return 0;
// If the result is already computed, return it
if (dp[k] !== -1)
return dp[k];
// Variable to store maximum score
let ans = Number.NEGATIVE_INFINITY;
// Pick 1 stone
ans = Math.max(ans, arr[k] - maximumStonesUtil(arr, k + 1, dp));
// Pick 2 stones
if (k + 1 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
- maximumStonesUtil(arr, k + 2, dp));
// Pick 3 stones
if (k + 2 < n)
ans = Math.max(ans, arr[k] + arr[k + 1]
+ arr[k + 2] - maximumStonesUtil(arr, k + 3, dp));
// Store result in dp and return
dp[k] = ans;
return dp[k];
}
// Function to find the winner of the game
function maximumStones(arr) {
const n = arr.length;
// Create a 1D DP table
const dp = new Array(n).fill(-1);
// Store the result
const res = maximumStonesUtil(arr, 0, dp);
// Determine winner
if (res > 0)
return 1;
else if (res < 0)
return 2;
else
return 0;
}
// Driver Code
const arr = [1, 2, 3, 7];
console.log(maximumStones(arr));
Output
2
Time Complexity: O(n)
Auxiliary Space: O(n)
[Efficient Approach II]: Using the DP ( bottom-up approach )
Here a another approach to solving this problem, but using the DP Tabulation (bottom-up) approach as it is showing overlapping subproblems properties, making it a perfect fit for DP.
This approach is better than pervious one as the memoized version uses recursion, which adds extra stack space and function call overhead and in this method we build the DP table iteratively from the end so it runs with the same time complexity but uses less memory and avoids the cost of recursive 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
- return the final result
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// Function to find the winner of game
int maximumStones(vector<int>& arr)
{
int n = arr.size();
// Create a 1D table, dp of size N+3
vector<int> dp(n + 3, 0);
// Initialize the table for the last 3 stones
dp[n] = dp[n + 1] = dp[n + 2] = 0;
// Calculate the table for the remaining stones in reverse order
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, b, c});
}
// Determine the winner
if (dp[0] > 0)
return 1;
else if (dp[0] < 0)
return 2;
else
return 0;
}
// Driver Code
int main()
{
vector<int> arr = {1, 2, 3, 7};
cout << maximumStones(arr);
return 0;
}
import java.util.*;
public class Main {
// Function to find the winner of the game
public static int maximumStones(int[] arr) {
int n = arr.length;
// Create a 1D table, dp of size N+3
int[] dp = new int[n + 3];
// Initialize the table for the last 3 stones
dp[n] = dp[n + 1] = dp[n + 2] = 0;
// Calculate the table for the remaining stones in reverse order
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));
}
// Determine the winner
if (dp[0] > 0)
return 1;
else if (dp[0] < 0)
return 2;
else
return 0;
}
// Driver Code
public static void main(String[] args) {
int[] arr = {1, 2, 3, 7};
System.out.println(maximumStones(arr));
}
}
def maximumStones(arr):
n = len(arr)
# Create dp array of size n + 3
dp = [0] * (n + 3)
# Initialize the table for the last 3 stones
dp[n] = dp[n + 1] = dp[n + 2] = 0
# Calculate the table for the remaining stones in reverse order
for i in range(n - 1, -1, -1):
a = arr[i] - dp[i + 1]
b, c = -1, -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, b, c)
# Determine the winner
if dp[0] > 0:
return 1
elif dp[0] < 0:
return 2
else:
return 0
# Driver Code
arr = [1, 2, 3, 7]
print(maximumStones(arr))
using System;
class Program
{
// Function to find the winner of the game
static int MaximumStones(int[] arr)
{
int n = arr.Length;
// Create a 1D table, dp of size N+3
int[] dp = new int[n + 3];
// Initialize the table for the last 3 stones
dp[n] = dp[n + 1] = dp[n + 2] = 0;
// Calculate the table for the remaining stones in reverse order
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));
}
// Determine the winner
if (dp[0] > 0)
return 1;
else if (dp[0] < 0)
return 2;
else
return 0;
}
// Driver Code
static void Main()
{
int[] arr = { 1, 2, 3, 7 };
Console.WriteLine(MaximumStones(arr));
}
}
// Function to find the winner of the game
function maximumStones(arr) {
const n = arr.length;
// Create a 1D table, dp of size N+3
const dp = new Array(n + 3).fill(0);
// Initialize the table for the last 3 stones
dp[n] = dp[n + 1] = dp[n + 2] = 0;
// Calculate the table for the remaining stones in reverse order
for (let i = n - 1; i >= 0; i--) {
let a = arr[i] - dp[i + 1];
let 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, b, c);
}
// Determine the winner
if (dp[0] > 0)
return 1;
else if (dp[0] < 0)
return 2;
else
return 0;
}
// Driver Code
const arr = [1, 2, 3, 7];
console.log(maximumStones(arr));
Output
2
Time Complexity: O(n)
Auxiliary Space: O(n)