Given a queue of n people, indexed from 0 to n - 1. Each person has a rating represented by an array arr[], where arr[i] represents the rating of the i-th person. The skill of a person depends not only on their rating but also on their immediate neighbours.
If you remove the i-th person from the queue, you gain a skill value of arr[i - 1] * arr[i] * arr[i + 1]. If i - 1 or i + 1 is out of bounds, assume there is an implicit person with a rating of 1 at that position.
Your task is to remove all n people one by one in an optimal order to maximize the total skill value. After removing a person, the remaining queue should be rearranged in their original order before the next removal.
Return the maximum total skill you can obtain by removing the people optimally.
Examples :
Input: arr[] = [5, 10]
Output: 60
Explanation:
- Remove person with rating 5 → Skill gained = 1 * 5 * 10 = 50, remaining queue: [10].
- Remove person with rating 10 → Skill gained = 1 * 10 * 1 = 10, total skill = 50 + 10 = 60.
Input: arr[] = [3, 2, 5, 8]
Output: 182
Explanation:
- Remove person with rating 2 → Skill gained = 3 * 2 * 5 = 30, remaining queue: [3, 5, 8].
- Remove person with rating 5 → Skill gained = 3 * 5 * 8 = 120, remaining queue: [3, 8].
- Remove person with rating 3 → Skill gained = 1 * 3 * 8 = 24, remaining queue: [8].
- Remove person with rating 8 → Skill gained = 1 * 8 * 1 = 8, total skill = 30 + 120 + 24 + 8 = 182
Table of Content
[Naive Approach] Using Recursion
To solve the problem using recursion, let's consider a recursive approach by focusing on each person removal individually and calculating the maximum skill at each step.
To maximize the total skill value, we can choose a person
ito remove last in a given range[left, right]. The skill from removingilast is given by: arr[left-1]*arr[i]*arr[right+1] plus the maximum skill from the left subarray[left, i-1]and right subarray[i+1, right].This gives the recurrence relation:
- maxSkill(l, r) = max(arr[l-1] * arr[i] * arr[r+1] + maxSkill(l, i-1) + maxSkill(i+1, r))
// C++ code to get maximum skill value
// by removing people using recursion.
#include <bits/stdc++.h>
using namespace std;
// Recursive function to calculate maximum
// skill from removing people in range [left, right]
int getMaxSkill(int left, int right, vector<int> &arr) {
// No people in this range
if (left > right) return 0;
int maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k`
// last in this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill from the left and
// right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr);
int rightSkill = getMaxSkill(k + 1, right, arr);
// Update maxSkill with the best option
maxSkill = max(maxSkill, skill + leftSkill + rightSkill);
}
return maxSkill;
}
// Function to calculate the maximum skill
// obtainable by removing all people
int maxSkill(vector<int> &arr) {
int n = arr.size();
// Add virtual people with rating
// 1 at both ends of `arr`
arr.insert(arr.begin(), 1);
arr.push_back(1);
// Call recursive helper on the
// full range of people
return getMaxSkill(1, n, arr);
}
int main() {
vector<int> arr = {3, 2, 5, 8};
cout << maxSkill(arr);
return 0;
}
// Java code to get maximum skill value
// by removing people using recursion.
import java.util.*;
class GfG {
// Recursive function to calculate maximum skill
// from removing people in range [left, right]
static int getMaxSkill(int left, int right, int[] arr) {
// No people in this range
if (left > right)
return 0;
int maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k` last in
// this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill from the left and
// right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr);
int rightSkill = getMaxSkill(k + 1, right, arr);
// Update maxSkill with the best option
maxSkill = Math.max(maxSkill, skill + leftSkill + rightSkill);
}
return maxSkill;
}
// Function to calculate the maximum skill obtainable
// by removing all people
static int maxSkill(int[] arr) {
int n = arr.length;
// Create a new array to store the original array
// plus virtual people at the ends
int[] nums = new int[n + 2];
// Add virtual people with rating 1
// at both ends of `nums`
nums[0] = 1;
nums[n + 1] = 1;
// Copy the elements of the original array into the
// new array
for (int i = 0; i < n; i++) {
nums[i + 1] = arr[i];
}
// Call recursive helper on the full range of
// people
return getMaxSkill(1, n, nums);
}
public static void main(String[] args) {
int[] arr = { 3, 2, 5, 8 };
System.out.println(maxSkill(arr));
}
}
# Python code to get maximum skill value
# by removing people using recursion.
# Recursive function to calculate maximum skill
# from removing people in range [left, right]
def getMaxSkill(left, right, arr):
# No people in this range
if left > right:
return 0
maxSkill = 0
# Try each person `k` in range [left, right]
# as the last person to remove
for k in range(left, right + 1):
# Calculate skill from removing `k` last in this range
skill = arr[left - 1] * arr[k] * arr[right + 1]
# Recursively compute skill from the left and
# right subarrays
leftSkill = getMaxSkill(left, k - 1, arr)
rightSkill = getMaxSkill(k + 1, right, arr)
# Update maxSkill with the best option
maxSkill = max(maxSkill, skill + leftSkill + rightSkill)
return maxSkill
# Function to calculate the maximum skill
# obtainable by removing all people
def maxSkill(arr):
# Add virtual people with rating 1 at both
# ends of `arr`
n = len(arr)
arr = [1] + arr + [1]
# Call recursive helper on the full
# range of people
return getMaxSkill(1, n, arr)
if __name__ == "__main__":
arr = [3, 2, 5, 8]
print(maxSkill(arr))
// C# code to get maximum skill value
// by removing people using recursion.
using System;
using System.Collections.Generic;
class GfG {
// Recursive function to calculate maximum skill
// from removing people in range [left, right]
static int getMaxSkill(int left, int right, List<int> arr) {
// No people in this range
if (left > right) return 0;
int maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k` last in this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill from the left
// and right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr);
int rightSkill = getMaxSkill(k + 1, right, arr);
// Update maxSkill with the best option
maxSkill = Math.Max(maxSkill, skill + leftSkill + rightSkill);
}
return maxSkill;
}
// Function to calculate the maximum skill
// obtainable by removing all people
static int maxSkill(List<int> arr) {
// Add virtual people with rating
// 1 at both ends of `arr`
int n = arr.Count;
arr.Insert(0, 1);
arr.Add(1);
// Call recursive helper on the full range
// of people
return getMaxSkill(1, n, arr);
}
static void Main() {
List<int> arr = new List<int> { 3, 2, 5, 8 };
Console.WriteLine(maxSkill(arr));
}
}
// JavaScript code to get maximum skill value
// by removing people using recursion.
// Recursive function to calculate maximum skill
// from removing people in range [left, right]
function getMaxSkill(left, right, arr) {
// No people in this range
if (left > right)
return 0;
let maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (let k = left; k <= right; k++) {
// Calculate skill from removing `k` last in this range
let skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill from the left and right
// subarrays
let leftSkill = getMaxSkill(left, k - 1, arr);
let rightSkill = getMaxSkill(k + 1, right, arr);
// Update maxSkill with the best option
maxSkill = Math.max(maxSkill, skill + leftSkill + rightSkill);
}
return maxSkill;
}
// Function to calculate the maximum skill
// obtainable by removing all people
function maxSkill(arr) {
// Add virtual people with rating 1 at both ends of `arr`
let n = arr.length;
arr.unshift(1);
arr.push(1);
// Call recursive helper on the full range of people
return getMaxSkill(1, n, arr);
}
// Driver Code
const arr = [3, 2, 5, 8];
console.log(maxSkill(arr));
Output
182
Time complexity: O(4n/(n3/2)), time complexity is exponential due to the recursive partitioning.
Auxiliary Space: O(n), recursive stack.
[Better Approach] Using Top-Down DP (Memoization) - O(n^3) Time and O(n^2) Space
If we notice carefully, we can observe that the above recursive solution holds the following two properties of Dynamic Programming:
- Optimal Substructure: TThe maximum skill value obtained from removing people in a range [left, right] depends on the optimal solution of subproblems [left, k-1] and [k+1, right], where k is the last person removed in the current range. By selecting each person k as the last to remove in the current range, we can combine the results of these subproblems to determine the maximum skill achievable for the range [left, right].
- Overlapping Subproblems: In a purely recursive approach, the same subproblems are calculated multiple times. For example, when calculating the maximum skill for a range [1, n], the recursive calls may repeatedly calculate the maximum skill for the same subranges, such as [1, k-1] or [k+1, n]. This repetition leads to overlapping subproblems.
To avoid recomputing results for the same subproblems, we use a 2D DP array dp of size (n+2) × (n+2) (as virtual persons with a rating of 1 are also added at both ends of the arr[] to simplify the handling of boundary conditions), where dp[left][right] stores the maximum skill value obtainable from removing all people in the range [left, right].
We initialize all entries in dp to -1 and populate them using memoization as we calculate optimal subproblems.
#include <bits/stdc++.h>
using namespace std;
// Recursive function to calculate maximum skill value
// from removing people in range [left, right]
// with memoization (Top-Down DP)
int getMaxSkill(int left, int right, vector<int> &arr,
vector<vector<int>> &memo) {
// If no people in this range, return 0
if (left > right) return 0;
// If the result is already computed, return it
if (memo[left][right] != -1) return memo[left][right];
int maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill value from removing `k`
// last in this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill value from the left and
// right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr, memo);
int rightSkill = getMaxSkill(k + 1, right, arr, memo);
// Update maxSkill with the best option
maxSkill = max(maxSkill, skill + leftSkill + rightSkill);
}
// Store the result in the memoization table
memo[left][right] = maxSkill;
return maxSkill;
}
// Function to calculate the maximum skill value
// obtainable by removing all people
int maxSkill(vector<int> &arr) {
int n = arr.size();
// Add virtual persons with value 1 at
// both ends of `arr`
arr.insert(arr.begin(), 1);
arr.push_back(1);
// Create a memoization table initialized to -1
vector<vector<int>> memo(n + 2, vector<int>(n + 2, -1));
// Call the recursive helper on the full
// range of people
return getMaxSkill(1, n, arr, memo);
}
int main() {
vector<int> arr = {3, 2, 5, 8};
cout << maxSkill(arr);
return 0;
}
class GfG {
// Recursive function to calculate maximum skill
// from removing people in range [left, right]
// with memoization (Top-Down DP)
static int getMaxSkill(int left, int right, int[] arr, int[][] memo) {
// If no people in this range, return 0
if (left > right)
return 0;
// If the result is already computed, return it
if (memo[left][right] != -1)
return memo[left][right];
int maxSkill = 0;
// Try each person `k` in range [left, right] as
// the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k` last in
// this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill from the left and
// right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr, memo);
int rightSkill = getMaxSkill(k + 1, right, arr, memo);
// Update maxSkill with the best option
maxSkill = Math.max(maxSkill, skill + leftSkill + rightSkill);
}
// Store the result in the memoization table
memo[left][right] = maxSkill;
return maxSkill;
}
// Function to calculate the maximum skill value
// obtainable by removing all people
static int maxSkill(int[] arr) {
// Add virtual people with value 1 at both ends of `arr`
int n = arr.length;
int[] newArr = new int[n + 2];
newArr[0] = 1;
for (int i = 0; i < n; i++) {
newArr[i + 1] = arr[i];
}
newArr[n + 1] = 1;
// Create a memoization table initialized to -1
int[][] memo = new int[n + 2][n + 2];
for (int i = 0; i < n + 2; i++) {
for (int j = 0; j < n + 2; j++) {
memo[i][j] = -1;
}
}
// Call the recursive helper on the full range of people
return getMaxSkill(1, n, newArr, memo);
}
public static void main(String[] args) {
int[] arr = { 3, 2, 5, 8 };
System.out.println(maxSkill(arr));
}
}
# Python code to get the maximum skill value by removing people.
# using memoization (Top-Down DP).
# Recursive function to calculate maximum skill
# from removing people in range [left, right]
# with memoization (Top-Down DP)
def getMaxSkill(left, right, arr, memo):
# If no people in this range, return 0
if left > right:
return 0
# If the result is already computed, return it
if memo[left][right] != -1:
return memo[left][right]
maxSkill = 0
# Try each person `k` in range [left, right]
# as the last person to remove
for k in range(left, right + 1):
# Calculate skill from removing `k`
# last in this range
skill = arr[left - 1] * arr[k] * arr[right + 1]
# Recursively compute skill from the left and
# right subarrays
leftSkill = getMaxSkill(left, k - 1, arr, memo)
rightSkill = getMaxSkill(k + 1, right, arr, memo)
# Update maxSkill with the best option
maxSkill = max(maxSkill, skill + leftSkill + rightSkill)
# Store the result in the memoization table
memo[left][right] = maxSkill
return maxSkill
# Function to calculate the maximum skill value
# obtainable by removing all people
def maxSkill(arr):
n = len(arr)
# Add virtual people with value 1 at
# both ends of `arr`
arr = [1] + arr + [1]
# Create a memoization table initialized to -1
memo = [[-1 for _ in range(n + 2)] for _ in range(n + 2)]
# Call the recursive helper on the full
# range of people
return getMaxSkill(1, n, arr, memo)
if __name__ == "__main__":
arr = [3, 2, 5, 8]
print(maxSkill(arr))
using System;
class GfG {
// Recursive function to calculate maximum skill value
// from removing people in range [left, right]
// with memoization (Top-Down DP)
static int getMaxSkill(int left, int right, int[] arr, int[,] memo) {
// If no people in this range, return 0
if (left > right)
return 0;
// If the result is already computed, return it
if (memo[left, right] != -1)
return memo[left, right];
int maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill value from removing `k` last in this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill value from the left and right subarrays
int leftSkill = getMaxSkill(left, k - 1, arr, memo);
int rightSkill = getMaxSkill(k + 1, right, arr, memo);
// Update maxSkill with the best option
maxSkill = Math.Max(maxSkill, skill + leftSkill + rightSkill);
}
// Store the result in the memoization table
memo[left, right] = maxSkill;
return maxSkill;
}
// Function to calculate the maximum skill value
// obtainable by removing all people
static int maxSkill(int[] arr) {
int n = arr.Length;
// Add virtual persons with value 1 at both ends of `arr`
int[] newArr = new int[n + 2];
newArr[0] = 1;
for (int i = 0; i < n; i++)
{
newArr[i + 1] = arr[i];
}
newArr[n + 1] = 1;
// Create a memoization table initialized to -1
int[,] memo = new int[n + 2, n + 2];
for (int i = 0; i < n + 2; i++)
{
for (int j = 0; j < n + 2; j++)
{
memo[i, j] = -1;
}
}
// Call the recursive helper on the full range of people
return getMaxSkill(1, n, newArr, memo);
}
static void Main() {
int[] arr = { 3, 2, 5, 8 };
Console.WriteLine(maxSkill(arr));
}
}
// JavaScript code to get the maximum skill value
// by removing people using memoization.
function getMaxSkill(left, right, arr, memo) {
// If no people in this range, return 0
if (left > right)
return 0;
// If the result is already computed, return it
if (memo[left][right] !== -1)
return memo[left][right];
let maxSkill = 0;
// Try each person `k` in range [left, right]
// as the last person to remove
for (let k = left; k <= right; k++) {
// Calculate skill value from removing `k` last in this range
let skill = arr[left - 1] * arr[k] * arr[right + 1];
// Recursively compute skill value from the left and right subarrays
let leftSkill = getMaxSkill(left, k - 1, arr, memo);
let rightSkill = getMaxSkill(k + 1, right, arr, memo);
// Update maxSkill with the best option
maxSkill = Math.max(maxSkill, skill + leftSkill + rightSkill);
}
// Store the result in the memoization table
memo[left][right] = maxSkill;
return maxSkill;
}
// Function to calculate the maximum skill value
// obtainable by removing all people
function maxSkill(arr) {
let n = arr.length;
// Add virtual persons with value 1 at both ends of `arr`
arr = [1, ...arr, 1];
// Create a memoization table initialized to -1
let memo = Array.from({ length: n + 2 }, () => Array(n + 2).fill(-1));
// Call the recursive helper on the full range of people
return getMaxSkill(1, n, arr, memo);
}
// Driver Code
let arr = [3, 2, 5, 8];
console.log(maxSkill(arr));
Output
182
[Expected Approach] Using Bottom-Up DP (Tabulation) - O(n^3) Time and O(n^2) Space
The approach is similar to the previous one. However, instead of breaking down the problem recursively, we iteratively build up the solution by calculating it in a bottom-up manner.
We maintain a dp[][] table such that
dp[i][j]stores the maximum skill value that can be obtained by removing all the people between indexiandj, inclusive.This eliminates the need for recursion and avoids redundant computations, making the solution more efficient. The bottom-up dynamic programming (DP) approach ensures that smaller subproblems are solved first, and their results are used to construct solutions for larger subarrays
// C++ program to find the maximum skill by removing
// people using tabulation (bottom-up dynamic programming):
#include <bits/stdc++.h>
using namespace std;
// Function to calculate maximum skill value
// from removing people in range [left, right]
int maxSkill(vector<int> &arr) {
int n = arr.size();
// Add virtual people with skill value 1 at both
// ends of `arr`
arr.insert(arr.begin(), 1);
arr.push_back(1);
// Initialize the dp table with 0
vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
// Iterate over subarrays of increasing length
for (int length = 1; length <= n; length++) {
for (int left = 1; left <= n - length + 1; left++) {
int right = left + length - 1;
// Try every possible last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k`
// last in this range
int skill = arr[left - 1] * arr[k] * arr[right + 1];
// Add skill from the left and right
// subarrays
int totalSkill = skill + dp[left][k - 1] + dp[k + 1][right];
// Update dp[left][right] with the maximum skill
dp[left][right] = max(dp[left][right], totalSkill);
}
}
}
// Return the result from the dp table for
// the full range of people
return dp[1][n];
}
int main() {
vector<int> arr = {3, 2, 5, 8};
cout << maxSkill(arr);
return 0;
}
// Java program to find the maximum skill by removing
// people using tabulation (bottom-up dynamic programming):
class GfG {
// Function to calculate maximum skill value
// from removing people in range [left, right]
static int maxSkill(int[] arr) {
int n = arr.length;
// Add virtual people with skill value 1 at both ends
int[] nums = new int[n + 2];
nums[0] = 1;
nums[n + 1] = 1;
for (int i = 0; i < n; i++) {
nums[i + 1] = arr[i];
}
// Initialize the dp table with 0
int[][] dp = new int[n + 2][n + 2];
// Iterate over subarrays of increasing length
for (int length = 1; length <= n; length++) {
for (int left = 1; left <= n - length + 1; left++) {
int right = left + length - 1;
// Try every possible last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k` last in this range
int skill = nums[left - 1] * nums[k] * nums[right + 1];
// Add skill from the left and right subarrays
int totalSkill = skill + dp[left][k - 1] + dp[k + 1][right];
// Update dp[left][right] with the maximum skill
dp[left][right] = Math.max(dp[left][right], totalSkill);
}
}
}
// Return the result from the dp table for the full range of people
return dp[1][n];
}
public static void main(String[] args) {
int[] arr = { 3, 2, 5, 8 };
System.out.println(maxSkill(arr));
}
}
# Python program to find the maximum skill by removing
# people using tabulation (bottom-up dynamic programming):
# Function to calculate maximum skill value
# from removing people in range [left, right]
def maxSkill(arr):
n = len(arr)
# Add virtual people with skill value 1 at both ends
arr = [1] + arr + [1]
# Initialize the dp table with 0
dp = [[0] * (n + 2) for _ in range(n + 2)]
# Iterate over subarrays of increasing length
for length in range(1, n + 1):
for left in range(1, n - length + 2):
right = left + length - 1
# Try every possible last person to remove
for k in range(left, right + 1):
# Calculate skill from removing `k` last in this range
skill = arr[left - 1] * arr[k] * arr[right + 1]
# Add skill from the left and right subarrays
totalSkill = skill + dp[left][k - 1] + dp[k + 1][right]
# Update dp[left][right] with the maximum skill
dp[left][right] = max(dp[left][right], totalSkill)
# Return the result from the dp table for the
# full range of people
return dp[1][n]
if __name__ == "__main__":
arr = [3, 2, 5, 8]
print(maxSkill(arr))
// C# program to find the maximum skill by removing
// people using tabulation (bottom-up dynamic programming):
using System;
class GfG {
// Function to calculate maximum skill value
// from removing people in range [left, right]
static int maxSkill(int[] arr) {
// Add virtual people with skill value 1 at both ends
int n = arr.Length;
int[] skills = new int[n + 2];
skills[0] = 1;
skills[n + 1] = 1;
for (int i = 0; i < n; i++) {
skills[i + 1] = arr[i];
}
// Initialize the dp table with 0
int[,] dp = new int[n + 2, n + 2];
// Iterate over subarrays of increasing length
for (int length = 1; length <= n; length++) {
for (int left = 1; left <= n - length + 1; left++) {
int right = left + length - 1;
// Try every possible last person to remove
for (int k = left; k <= right; k++) {
// Calculate skill from removing `k`
// last in this range
int skill = skills[left - 1] *
skills[k] * skills[right + 1];
// Add skill from the left and right subarrays
int totalSkill = skill +
dp[left, k - 1] + dp[k + 1, right];
// Update dp[left, right] with the maximum skill
dp[left, right] = Math.Max(dp[left, right], totalSkill);
}
}
}
// Return the result from the dp table
// for the full range of people
return dp[1, n];
}
static void Main() {
int[] arr = { 3, 2, 5, 8 };
Console.WriteLine(maxSkill(arr));
}
}
// JavaScript program to find the maximum skill by removing
// people using tabulation (bottom-up dynamic programming):
function maxSkill(arr) {
let n = arr.length;
// Add virtual people with skill value 1 at both ends
arr = [1, ...arr, 1];
// Initialize the dp table with 0
let dp = Array.from({ length: n + 2 }, () => Array(n + 2).fill(0));
// Iterate over subarrays of increasing length
for (let length = 1; length <= n; length++) {
for (let left = 1; left <= n - length + 1; left++) {
let right = left + length - 1;
// Try every possible last person to remove
for (let k = left; k <= right; k++) {
// Calculate skill from removing `k` last in this range
let skill = arr[left - 1] * arr[k] * arr[right + 1];
// Add skill from the left and right subarrays
let totalSkill = skill + dp[left][k - 1] + dp[k + 1][right];
// Update dp[left][right] with the maximum skill
dp[left][right] = Math.max(dp[left][right], totalSkill);
}
}
}
// Return the result from the dp table for the full range of people
return dp[1][n];
}
// Driver Code
const arr = [3, 2, 5, 8];
console.log(maxSkill(arr));
Output
182