Maximize point to reduce Array by replacing Subarray with its sum
Last Updated :
18 Sep, 2023
Given an array arr[] of size, N, the task is to maximize the score to reduce the array to a single element by replacing any subarray with its sum where the score of one such operation is the product of subarray length and the minimum value of that subarray.
Examples:
Input: N = 2, arr[] = {1, 5}
Output: 2
Explanation: Select subarray (0, 1). Then array arr[] become { 6 } and points earned = 2 * min(1, 5) = 2. Total points earned = 2
Input: N = 3, arr[] = {2, 3, 5}
Output: 14
Explanation: First select subarray (0, 1), then array arr[] become { 5, 5 } and points earned = 2 * min(2, 3) = 4. Select subarray (0, 1), then array arr[] become { 10 } and points earned = 2* min(5, 5) = 10. Total points earned = 4 + 10 =14.
Approach using Prefix Sum and DP (Memoization):
The Basic idea to solve this problem statement is to maintain a prefix sum array and obtain sum between range using DP (memoization).
Let there be three elements in the subarray {a, b, c} with b > a > c.
1st Case: If we include all the element at a time. array becomes {a+b+c}, points reward = 3*min(a, b, c) = 3*c.
2nd Case: Now, take two element at a time,
First select subarray(1, 2), array becomes (a, b+c), points reward = 2 * min(b, c) = 2*c
Now select subarray(0, 1), array becomes (a+b+c), points reward = 2 * min(a, b+c) = 2*a
Total points obtained = 2*c + 2*a
Now, points obtained in the 2nd case is more than points obtained in the first case as
a > c So 2*a > c. So it is optimal to not choose all of the three at a time and break it into two parts from b. This can be implemented with the help of dynamic programming to store the maximum possible point for a subarray [i, j] for avoiding repeated calculation.
Follow the steps mentioned below to implement the idea:
- Create a prefix sum array pre[] to obtain the sum between any range in O(1) time.
- Create a recursive function that takes i and j as parameters that determines the range of a group.
- Iterate from k = i to j to partition the given range into two groups.
- Call the recursive function for these groups.
- Suppose ans1and ans2 represents the total points obtained from the left and the right part respectively.
- Now for the current partition at index k, the potential answer will be ans1 + ans2 + 2* min(left element, right element) as seen from the above discussion.
- Now left element will be pre[k+1]-pre[i], the right element will be pre[j-1] – pre[k+1], which can be computed in O(1) time from the prefix sum array.
- The Maximum value obtained for the range 0 to N-1 is the required answer.
Below is the implementation of the above approach.
C++
#include <bits/stdc++.h>
using namespace std;
long long dp[100][100];
long long maxPoint( int i, int j, vector< long long >& arr,
vector< long long >& temp)
{
if (i >= j)
return 0;
if (j - i == 1)
return 2 * min(arr[i], arr[j]);
if (dp[i][j] != -1)
return dp[i][j];
long long ans = 0;
for ( int k = i; k < j; k++) {
long long ans1 = maxPoint(i, k, arr, temp);
long long ans2 = maxPoint(k + 1, j, arr, temp);
long long left_element = temp[k + 1] - temp[i];
long long right_element = temp[j + 1] - temp[k + 1];
long long total
= ans1 + ans2
+ min(left_element, right_element) * 2;
ans = max(total, ans);
}
return dp[i][j] = ans;
}
int findMax(vector< long long >& arr, int N)
{
vector< long long > temp;
temp.push_back(0);
long long s = 0;
for ( int i = 0; i < N; i++) {
s += arr[i];
temp.push_back(s);
}
memset (dp, -1, sizeof (dp));
return maxPoint(0, N - 1, arr, temp);
}
int main()
{
vector< long long > arr = { 2, 3, 5 };
int N = arr.size();
int ans = findMax(arr, N);
cout << ans;
return 0;
}
|
Java
public class gfg2
{
static long [][] dp = new long [ 100 ][ 100 ];
static long maxPoint( int i, int j, long [] arr,
long [] temp)
{
if (i >= j)
return 0 ;
if (j - i == 1 )
return 2 * Math.min(arr[i], arr[j]);
if (dp[i][j] != - 1 )
return dp[i][j];
long ans = 0 ;
for ( int k = i; k < j; k++) {
long ans1 = maxPoint(i, k, arr, temp);
long ans2 = maxPoint(k + 1 , j, arr, temp);
long left_element = temp[k + 1 ] - temp[i];
long right_element = temp[j + 1 ] - temp[k + 1 ];
long total
= ans1 + ans2
+ Math.min(left_element, right_element)
* 2 ;
ans = Math.max(total, ans);
}
return dp[i][j] = ans;
}
static long findMax( long [] arr, int N)
{
long [] temp = new long [N + 1 ];
long s = 0 ;
for ( int i = 0 ; i < N; i++) {
s += arr[i];
temp[i + 1 ] = s;
}
for ( int i = 0 ; i < dp.length; i++) {
for ( int j = 0 ; j < dp[ 0 ].length; j++) {
dp[i][j] = - 1 ;
}
}
return maxPoint( 0 , N - 1 , arr, temp);
}
public static void main(String[] args)
{
long [] arr = { 2 , 3 , 5 };
int N = arr.length;
long ans = findMax(arr, N);
System.out.println(ans);
}
}
|
Python3
dp = [[ 0 for i in range ( 100 )] for j in range ( 100 )]
def maxPoint(i,j,arr,temp):
if (i> = j):
return 0
if (j - i = = 1 ):
return 2 * min (arr[i], arr[j])
if (dp[i][j] ! = - 1 ):
return dp[i][j]
ans = 0
for k in range (i,j):
ans1 = maxPoint(i, k, arr, temp)
ans2 = maxPoint(k + 1 , j, arr, temp)
left_element = temp[k + 1 ] - temp[i]
right_element = temp[j + 1 ] - temp[k + 1 ]
total = ans1 + ans2 + min (left_element, right_element) * 2
ans = max (total, ans)
dp[i][j] = ans
return dp[i][j]
def findMax(arr,N):
temp = []
temp.append( 0 )
s = 0
for i in range (N):
s = s + arr[i]
temp.append(s)
for i in range ( len (dp)):
for j in range ( len (dp[ 0 ])):
dp[i][j] = - 1
return maxPoint( 0 , N - 1 , arr, temp)
arr = [ 2 , 3 , 5 ]
N = len (arr)
ans = findMax(arr,N)
print (ans)
|
C#
using System;
public class GFG {
static long [, ] dp = new long [100, 100];
static long maxPoint( int i, int j, long [] arr,
long [] temp)
{
if (i >= j)
return 0;
if (j - i == 1)
return 2 * Math.Min(arr[i], arr[j]);
if (dp[i, j] != -1)
return dp[i, j];
long ans = 0;
for ( int k = i; k < j; k++) {
long ans1 = maxPoint(i, k, arr, temp);
long ans2 = maxPoint(k + 1, j, arr, temp);
long left_element = temp[k + 1] - temp[i];
long right_element = temp[j + 1] - temp[k + 1];
long total
= ans1 + ans2
+ Math.Min(left_element, right_element)
* 2;
ans = Math.Max(total, ans);
}
return dp[i, j] = ans;
}
static long findMax( long [] arr, int N)
{
long [] temp = new long [N + 1];
long s = 0;
for ( int i = 0; i < N; i++) {
s += arr[i];
temp[i + 1] = s;
}
for ( int i = 0; i < dp.GetLength(0); i++) {
for ( int j = 0; j < dp.GetLength(1); j++) {
dp[i, j] = -1;
}
}
return maxPoint(0, N - 1, arr, temp);
}
static public void Main()
{
long [] arr = { 2, 3, 5 };
int N = arr.Length;
long ans = findMax(arr, N);
Console.WriteLine(ans);
}
}
|
Javascript
var dp = [];
function maxPoint(i, j, arr, temp) {
if (i >= j)
return 0;
if (j - i == 1)
return 2 * Math.min(arr[i], arr[j]);
if (dp[i][j] != -1)
return dp[i][j];
var ans = 0;
for ( var k = i; k < j; k++) {
var ans1 = maxPoint(i, k, arr, temp);
var ans2 = maxPoint(k + 1, j, arr, temp);
var left_element = temp[k + 1] - temp[i];
var right_element = temp[j + 1] - temp[k + 1];
var total = ans1 + ans2 + Math.min(left_element, right_element) * 2;
ans = Math.max(total, ans);
}
return dp[i][j] = ans;
}
function findMax(arr, N) {
var temp = [];
temp.push(0);
var s = 0;
for ( var i = 0; i < N; i++) {
s += arr[i];
temp.push(s);
}
for ( var i = 0; i < 100; i++) {
dp[i] = [];
for ( var j = 0; j < 100; j++) {
dp[i][j] = -1;
}
}
return maxPoint(0, N - 1, arr, temp);
}
var arr = [2, 3, 5];
var N = arr.length;
var ans = findMax(arr, N);
console.log(ans);
|
Time complexity: O(N2)
Auxiliary space: O(N2)
Efficient approach : Using DP Tabulation method ( Iterative approach )
The approach to solve this problem is same but DP tabulation(bottom-up) method is better then Dp + memoization(top-down) because memoization method needs extra stack space of recursion calls.
Below is the implementation of the code:
C++
#include <bits/stdc++.h>
using namespace std;
int findMax(vector< long long >& arr, int N)
{
vector< long long > temp(N+1, 0);
long long s = 0;
for ( int i = 0; i < N; i++) {
s += arr[i];
temp[i+1] = s;
}
vector<vector< long long >> dp(N, vector< long long >(N, 0));
for ( int len = 1; len < N; len++) {
for ( int i = 0; i < N-len; i++) {
int j = i + len;
if (len == 1) {
dp[i][j] = 2 * min(arr[i], arr[j]);
} else {
for ( int k = i; k < j; k++) {
long long ans1 = dp[i][k];
long long ans2 = dp[k+1][j];
long long left_element = temp[k+1] - temp[i];
long long right_element = temp[j+1] - temp[k+1];
long long total = ans1 + ans2 + 2 * min(left_element, right_element);
dp[i][j] = max(dp[i][j], total);
}
}
}
}
return dp[0][N-1];
}
int main() {
vector< long long > arr = {2, 3, 5};
int N = arr.size();
int ans = findMax(arr, N);
cout << ans;
return 0;
}
|
Java
import java.util.*;
public class MaxPossiblePoints {
static int findMax(List<Long> arr, int N) {
List<Long> temp = new ArrayList<>(Collections.nCopies(N + 1 , 0L));
long s = 0 ;
for ( int i = 0 ; i < N; i++) {
s += arr.get(i);
temp.set(i + 1 , s);
}
long [][] dp = new long [N][N];
for ( int len = 1 ; len < N; len++) {
for ( int i = 0 ; i < N - len; i++) {
int j = i + len;
if (len == 1 ) {
dp[i][j] = 2 * Math.min(arr.get(i), arr.get(j));
} else {
for ( int k = i; k < j; k++) {
long ans1 = dp[i][k];
long ans2 = dp[k + 1 ][j];
long leftElement = temp.get(k + 1 ) - temp.get(i);
long rightElement = temp.get(j + 1 ) - temp.get(k + 1 );
long total = ans1 + ans2 + 2 * Math.min(leftElement, rightElement);
dp[i][j] = Math.max(dp[i][j], total);
}
}
}
}
return ( int ) dp[ 0 ][N - 1 ];
}
public static void main(String[] args) {
List<Long> arr = new ArrayList<>(Arrays.asList(2L, 3L, 5L));
int N = arr.size();
int ans = findMax(arr, N);
System.out.println(ans);
}
}
|
Python3
def findMax(arr, N):
temp = [ 0 ] * (N + 1 )
s = 0
for i in range (N):
s + = arr[i]
temp[i + 1 ] = s
dp = [[ 0 ] * N for _ in range (N)]
for length in range ( 1 , N):
for i in range (N - length):
j = i + length
if length = = 1 :
dp[i][j] = 2 * min (arr[i], arr[j])
else :
for k in range (i, j):
ans1 = dp[i][k]
ans2 = dp[k + 1 ][j]
left_element = temp[k + 1 ] - temp[i]
right_element = temp[j + 1 ] - temp[k + 1 ]
total = ans1 + ans2 + 2 * min (left_element, right_element)
dp[i][j] = max (dp[i][j], total)
return dp[ 0 ][N - 1 ]
arr = [ 2 , 3 , 5 ]
N = len (arr)
ans = findMax(arr, N)
print (ans)
|
C#
using System;
using System.Collections.Generic;
class GFG
{
static int findMax(List< long > arr, int N)
{
List< long > temp = new List< long >( new long [N + 1]);
long s = 0;
for ( int i = 0; i < N; i++)
{
s += arr[i];
temp[i + 1] = s;
}
long [][] dp = new long [N][];
for ( int i = 0; i < N; i++)
{
dp[i] = new long [N];
for ( int j = 0; j < N; j++)
{
dp[i][j] = 0;
}
}
for ( int len = 1; len < N; len++)
{
for ( int i = 0; i < N - len; i++)
{
int j = i + len;
if (len == 1)
{
dp[i][j] = 2 * Math.Min(arr[i], arr[j]);
}
else
{
for ( int k = i; k < j; k++)
{
long ans1 = dp[i][k];
long ans2 = dp[k + 1][j];
long left_element = temp[k + 1] - temp[i];
long right_element = temp[j + 1] - temp[k + 1];
long total = ans1 + ans2 + 2 * Math.Min(left_element, right_element);
dp[i][j] = Math.Max(dp[i][j], total);
}
}
}
}
return ( int )dp[0][N - 1];
}
static void Main( string [] args)
{
List< long > arr = new List< long > { 2, 3, 5 };
int N = arr.Count;
int ans = findMax(arr, N);
Console.WriteLine(ans);
}
}
|
Javascript
function findMax(arr, N) {
let temp = new Array(N + 1).fill(0);
let s = 0;
for (let i = 0; i < N; i++) {
s += arr[i];
temp[i + 1] = s;
}
let dp = new Array(N);
for (let i = 0; i < N; i++) {
dp[i] = new Array(N).fill(0);
}
for (let len = 1; len < N; len++) {
for (let i = 0; i < N - len; i++) {
let j = i + len;
if (len === 1) {
dp[i][j] = 2 * Math.min(arr[i], arr[j]);
} else {
for (let k = i; k < j; k++) {
let ans1 = dp[i][k];
let ans2 = dp[k + 1][j];
let left_element = temp[k + 1] - temp[i];
let right_element = temp[j + 1] - temp[k + 1];
let total = ans1 + ans2 + 2 * Math.min(left_element, right_element);
dp[i][j] = Math.max(dp[i][j], total);
}
}
}
}
return dp[0][N - 1];
}
let arr = [2, 3, 5];
let N = arr.length;
let ans = findMax(arr, N);
console.log(ans);
|
Time complexity: O(N^2)
Auxiliary space: O(N^2)
Related Articles:
Share your thoughts in the comments
Please Login to comment...