Given an array numbers[] of several numbers that may repeat. You are allowed to remove similar numbers in a range, the score you get after removing these numbers is let k be the number removed from the range then your score increases by k*k. You need to return the maximum score you can get after all the numbers are removed. Consider the examples for more clarity.
Examples:
Input: numbers = {1, 3, 2, 3, 2, 1, 1, 1}
Output: 22
Explanation: The order in which we will follow the removals is as follows, first, we remove 2 at index (2) which adds to 1*1 because we removed just one integer, then the new array is {1, 3, 3, 2, 1, 1, 1}, now we will again remove 2 at index (3) this again adds to 1*1, the new array is {1, 3, 3, 1, 1, 1}, we now remove the range (1-2) the two 3’s, that adds up to 2*2 because we removed 2 numbers, now our array becomes {1,1,1,1} and now we remove the range (0,3) which adds up 4*4 in our sum and now our array is empty. The total sum comprises ( 1*1 + 1*1 + 2*2 + 4*4 ) = 22.Input: numbers = {1, 1, 1, 2}
Output: 10
Explanation: We at first removed 2 which is at the last index which adds up to 1*1, then our array is {1,1,1} and now we remove the entire range which adds up to 3*3 to our answer. now our answer consist of (1*1 + 3*3 ) = 10.
Approach: To solve the problem follow the below steps:
-
Function Signature:
- int removeNumbers(vector<int>& numbers): The function takes a vector of integers numbers as input and returns an integer.
-
Memoization:
- The code uses an unordered map unordered_map<int,int> dp for memoization. It stores the result of previously computed states to avoid redundant computations.
-
Recursive Solver Function:
- int solver(vector<int>& numbers, int l, int r, int k): This function recursively calculates the maximum sum of squares for a given range [l, r] and a parameter k.
-
Parameters:
- numbers: The input vector of numbers.
- l: The left index of the current range.
- r: The right index of the current range.
- k: A parameter used to track consecutive repeated numbers.
-
Base Case:
- If l > r, the range is empty, so the function returns 0.
-
Memoization Key:
- The memoization key is computed as (l * numbers.size() + r) * numbers.size() + k. It’s designed to uniquely identify each subproblem based on its parameters.
-
Memoization Check:
- Before performing any computations, the function checks if the result for the current state is already computed and stored in dp. If so, it returns the stored result.
-
Consecutive Repeated Numbers:
- The function checks for consecutive repeated numbers at both ends of the current range [l, r]. It updates k accordingly.
-
Recursion for Subproblems:
-
The function recursively explores two cases:
- Removing the rightmost element (solver(numbers, l, r-1, 0)) and adding k+1 squared to the result.
- Splitting the range at position i and recursively solving for both subranges (solver(numbers, l, i, k+1) + solver(numbers, i+1, r-1, 0)).
-
The function recursively explores two cases:
-
Memoization Update:
- The function updates dp[key] with the maximum result obtained from the above two cases.
-
Return Value:
- The function returns dp[key], which represents the maximum sum of squares for the given range [l, r] and parameter k.
-
Main Function:
- In the main() function, a sample vector numbers is provided, and the removeNumbers function is called to solve the problem.
Below is the implementation of above approach:
// Include necessary header files #include <bits/stdc++.h> using namespace std;
unordered_map< int , int > dp;
// Recursive function to solve the problem int solver(vector< int >& numbers, int l, int r, int k)
{ // Base case: if left index exceeds
// right index, return 0
if (l > r)
return 0;
// Compute a unique key for memoization
int key = (l * numbers.size() + r) * numbers.size() + k;
// Check if result for current state
// is already computed
if (dp.find(key) != dp.end())
return dp[key];
// Handle consecutive repeated numbers
// at the right end of the range
while (r > l and numbers[r] == numbers[r - 1]) {
r--;
k++;
}
// Handle consecutive repeated numbers
// at the left end of the range
while (r > l and numbers[l] == numbers[r]) {
l++;
k++;
}
// Update key after handling
// consecutive repeats
key = (l * numbers.size() + r) * numbers.size() + k;
// Calculate result by recursively
// solving subproblems
dp[key]
= solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1);
for ( int i = l; i < r; i++) {
if (numbers[i] == numbers[r]) {
while (i + 1 < r
and numbers[i + 1] == numbers[r])
i++;
// After taking similar characters
// from left let the left range be
// partof the right range and hence
// add K+1 to previously going on
// range, and for the range that is
// left start from k=0 and try
// removing new sub ranges of similar
// numbers from this range.
dp[key]
= max(dp[key], solver(numbers, l, i, k + 1)
+ solver(numbers, i + 1,
r - 1, 0));
}
}
// Return the computed result
// for this state
return dp[key];
} // Wrapper function to initiate // the solving process int removeNumbers(vector< int >& numbers)
{ return solver(numbers, 0, numbers.size() - 1, 0);
} // Drivers code int main()
{ // Example usage
vector< int > numbers = { 1, 2, 3, 2, 1, 1, 1 };
// Drivers code
cout << removeNumbers(numbers);
return 0;
} |
// Java Implementation import java.util.HashMap;
import java.util.Map;
public class Main {
// Memoization map to store computed results
static Map<Integer, Integer> dp = new HashMap<>();
// Recursive function to solve the problem
public static int solver( int [] numbers, int l, int r, int k) {
// Base case: if left index exceeds right index, return 0
if (l > r) {
return 0 ;
}
// Compute a unique key for memoization
int key = (l * numbers.length + r) * numbers.length + k;
// Check if result for current state is already computed
if (dp.containsKey(key)) {
return dp.get(key);
}
// Handle consecutive repeated numbers at the right end of the range
while (r > l && numbers[r] == numbers[r - 1 ]) {
r--;
k++;
}
// Handle consecutive repeated numbers at the left end of the range
while (r > l && numbers[l] == numbers[r]) {
l++;
k++;
}
// Update key after handling consecutive repeats
key = (l * numbers.length + r) * numbers.length + k;
// Calculate result by recursively solving subproblems
dp.put(key, solver(numbers, l, r - 1 , 0 ) + (k + 1 ) * (k + 1 ));
for ( int i = l; i < r; i++) {
if (numbers[i] == numbers[r]) {
while (i + 1 < r && numbers[i + 1 ] == numbers[r]) {
i++;
}
// After taking similar characters from left, let the left range be
// part of the right range and hence add K+1 to previously going on
// range, and for the range that is left, start from k=0 and try
// removing new subranges of similar numbers from this range.
dp.put(key, Math.max(dp.get(key), solver(numbers, l, i, k + 1 ) + solver(numbers, i + 1 , r - 1 , 0 )));
}
}
// Return the computed result for this state
return dp.get(key);
}
// Wrapper function to initiate the solving process
public static int removeNumbers( int [] numbers) {
return solver(numbers, 0 , numbers.length - 1 , 0 );
}
// Drivers code
public static void main(String[] args) {
// Example usage
int [] numbers = { 1 , 2 , 3 , 2 , 1 , 1 , 1 };
// Print the result
System.out.println(removeNumbers(numbers));
}
} // This code is contributed by Sakshi |
def solver(numbers, l, r, k, dp):
# Base case: if left index exceeds right index, return 0
if l > r:
return 0
# Compute a unique key for memoization
key = (l * len (numbers) + r) * len (numbers) + k
# Check if the result for the current state is already computed
if key in dp:
return dp[key]
# Handle consecutive repeated numbers at the right end of the range
while r > l and numbers[r] = = numbers[r - 1 ]:
r - = 1
k + = 1
# Handle consecutive repeated numbers at the left end of the range
while r > l and numbers[l] = = numbers[r]:
l + = 1
k + = 1
# Update key after handling consecutive repeats
key = (l * len (numbers) + r) * len (numbers) + k
# Calculate the result by recursively solving subproblems
dp[key] = solver(numbers, l, r - 1 , 0 , dp) + (k + 1 ) * (k + 1 )
for i in range (l, r):
if numbers[i] = = numbers[r]:
while i + 1 < r and numbers[i + 1 ] = = numbers[r]:
i + = 1
# After taking similar characters from the left,
# let the left range be part of the right range, and hence,
# add K+1 to the previously going on range,
# and for the range that is left, start from k=0 and try
# removing new sub-ranges of similar numbers from this range.
dp[key] = max (dp[key], solver(numbers, l, i, k + 1 , dp) +
solver(numbers, i + 1 , r - 1 , 0 , dp))
# Return the computed result for this state
return dp[key]
# Wrapper function to initiate the solving process def remove_numbers(numbers):
dp = {}
return solver(numbers, 0 , len (numbers) - 1 , 0 , dp)
# Example usage numbers = [ 1 , 2 , 3 , 2 , 1 , 1 , 1 ]
print (remove_numbers(numbers))
|
using System;
using System.Collections.Generic;
class Program
{ static Dictionary< int , int > dp = new Dictionary< int , int >();
static int Solver(List< int > numbers, int l, int r, int k)
{
// Base case: if left index exceeds
// right index, return 0
if (l > r)
return 0;
// Compute a unique key for memoization
int key = ((l * numbers.Count + r) * numbers.Count) + k;
// Check if result for current state
// is already computed
if (dp.ContainsKey(key))
return dp[key];
// Handle consecutive repeated numbers
// at the right end of the range
while (r > l && numbers[r] == numbers[r - 1])
{
r--;
k++;
}
// Handle consecutive repeated numbers
// at the left end of the range
while (r > l && numbers[l] == numbers[r])
{
l++;
k++;
}
// Update key after handling
// consecutive repeats
key = ((l * numbers.Count + r) * numbers.Count) + k;
// Calculate result by recursively
// solving subproblems
dp[key] = Solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1);
for ( int i = l; i < r; i++)
{
if (numbers[i] == numbers[r])
{
while (i + 1 < r && numbers[i + 1] == numbers[r])
i++;
// After taking similar characters
// from left let the left range be
// part of the right range and hence
// add K+1 to previously going on
// range, and for the range that is
// left start from k=0 and try
// removing new subranges of similar
// numbers from this range.
dp[key] = Math.Max(dp[key], Solver(numbers, l, i, k + 1) + Solver(numbers, i + 1, r - 1, 0));
}
}
// Return the computed result
// for this state
return dp[key];
}
// Wrapper function to initiate
// the solving process
static int RemoveNumbers(List< int > numbers)
{
return Solver(numbers, 0, numbers.Count - 1, 0);
}
// Driver code
static void Main()
{
// Example usage
List< int > numbers = new List< int > { 1, 2, 3, 2, 1, 1, 1 };
// Driver code
Console.WriteLine(RemoveNumbers(numbers));
}
} // This code is contributed by shivamgupta310570 |
// Using a Map for memoization let dp = new Map();
// Recursive function to solve the problem function solver(numbers, l, r, k) {
// Base case: if left index exceeds right index, return 0
if (l > r) {
return 0;
}
// Compute a unique key for memoization
let key = (l * numbers.length + r) * numbers.length + k;
// Check if result for current state is already computed
if (dp.has(key)) {
return dp.get(key);
}
// Handle consecutive repeated numbers at the right end of the range
while (r > l && numbers[r] === numbers[r - 1]) {
r--;
k++;
}
// Handle consecutive repeated numbers at the left end of the range
while (r > l && numbers[l] === numbers[r]) {
l++;
k++;
}
// Update key after handling consecutive repeats
key = (l * numbers.length + r) * numbers.length + k;
// Calculate result by recursively solving subproblems
dp.set(key, solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1));
for (let i = l; i < r; i++) {
if (numbers[i] === numbers[r]) {
while (i + 1 < r && numbers[i + 1] === numbers[r]) {
i++;
}
// After taking similar characters from left, let the left range be
// part of the right range and hence add K+1 to previously going on
// range. For the range that is left, start from k=0 and try
// removing new sub-ranges of similar numbers from this range.
dp.set(key, Math.max(dp.get(key), solver(numbers, l, i, k + 1) + solver(numbers, i + 1, r - 1, 0)));
}
}
// Return the computed result for this state
return dp.get(key);
} // Wrapper function to initiate the solving process function removeNumbers(numbers) {
return solver(numbers, 0, numbers.length - 1, 0);
} // Example usage let numbers = [1, 2, 3, 2, 1, 1, 1]; console.log(removeNumbers(numbers)); // This code is contributed by shivamgupta0987654321 |
21
Time Complexity: O(n^4), where n is the size of the input vector numbers, we can see that the function is called twice in the for loop , that makes O(n^2)^2 times, which is why O(n^4).
Auxiliary Space: O(n^3). This is because of the usage of the unordered map dp for memoization.