MaxSkill: Optimally Removing People to Maximise Skill (original) (raw)

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:

**Input: arr[] = [3, 2, 5, 8]
**Output: 182
**Explanation:

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 i to remove last in a given range [left, right]. The skill from removing i last 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:

C++ ``

// 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 &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 &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

// 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

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#

// 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

// 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));

``

**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:

  1. **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].
  2. **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.

C++ ``

#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 &arr, vector<vector> &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 &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;

}

Java

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

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))

C#

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

// 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));

``

[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 index i and j, 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++ ``

// 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 &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

// 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

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#

// 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

// 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));

``