Given an array A[] of length N. You can apply the below operation:
- Choose an index i such that (i+1) element exists. After that choose any number X (positive or negative). Then subtract X from the ith element and add it into (i+1)th element.
The task is to find the minimum number of operations required to make all array elements positive. If not possible output -1.
Examples:
Input: N = 3, A[] = {-3, 2, 1}
Output: 2
Explanation: The performed operations are as below:
- First operation: Choose i = 2 and X = -1. Then update A2 and A3 as 2-(-1)=3 and 1+(-1)=0 respectively. Then, updated A[] is {-3, 3, 0}
- Second operation: Choose i = 1 and X = -3. Then update A1 and A2 as -3-(-3) = 0 and 3+(-3)=0 respectively. Then, updated A[] is {0, 0, 0}. Now all the elements are >= 0. Therefore, minimum number of operations are 2.
Input: N = 2, A[] = {1, -2}
Output: -1
Explanation: It can be verified that using given operation it is not possible to make all Ai >=0.
Approach: Implement the idea below to solve the problem
The problem can be solved using Prefix Sums and LIS (Longest Increasing Subsequence) Concept. Let us see the significance of both. The main approach for solving the problem is to calculate
N - LIS[N-1],
w
hereLIS[N-1]
is the length of the longest increasing subsequence of the Prefix sum array, andN - LIS[N-1]
is the minimum number of elements that need to be removed to make the sum of the remaining elements non-negative.
- Prefix Sums: Prefix Sums is an array which gives the sum of input array till the ith index.
- Check for Negative Sums: If the total sum of the array (which is the last element of the prefix sum array
P
) is negative, Output-1.
Because in such cases, It will be impossible to make all elements non-negative, If the total sum is already negative.- Longest Increasing Subsequence (LIS): Two empty arrays let say Re
s
andLIS[]
of sizeN
. Iterates over the prefix sum arrayPre[]
and for each elementlet say X
, We need to check ifX
is negative. Ifit
is negative, We need to setLIS[i]
to0
and continues to the next iteration. Otherwise, in approach we uses the upper_bound() function to find the first element in Res
that is greater thanX
and replaces it withX
. If no such element is found, We need to appendX
to the end ofRes
. TheLIS
array stores the length of the longest increasing subsequence ending at each index.- Output: Finally, we need to output
N - LIS[N-1]
as required answer
, Which is the minimum number of operations required to make all elements of the array non-negative.
Steps taken to solve the problem:
- Calculate Prefix Sums: Create a new vector
P[]
of sizeN
for the prefix sums of the arrayA
. Which gives the sum of A[] till index i. - Check for Negative Sum: If the total sum of the array (which is the last element of the prefix sum array
P[]
) is negative, it’s impossible to make all elements non-negative. In this case, output-1
. - Initialize LIS Arrays: If the total sum is not negative, initialize two new arrays:
Res[]
andLIS[]
. Theres
array will store the longest increasing subsequence (LIS) of the prefix sum arrayP
, and theLIS
array will store the length of the LIS ending at each index. - Calculate LIS: Iterate over the prefix sum array
P
. For each elementX
, ifX
is negative, set the corresponding element inLIS
to0
and continue to the next iteration. Otherwise, find the first element inres
that is greater thanX
and replace it withX
. If no such element is found, appendX
to the end ofRes
. - Result: Finally, output the minimum number of operations required to make all elements of the array non-negative. This is given by
N - LIS[N-1].
Code to implement the approach:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
// Function to calculate minimum operations
int minOperations(vector<int> A)
{
int N = A.size();
// Calculate prefix sums
vector<long long> P(N);
P[0] = A[0];
for (int i = 1; i < N; i++) {
P[i] = A[i] + P[i - 1];
}
// If total sum is negative, return -1
if (P[N - 1] < 0) {
return -1;
}
// Initialize result and LIS arrays
vector<long long> res;
vector<int> lis(N);
// Calculate LIS of prefix sum array
for (int i = 0; i < N; i++) {
long long x = P[i];
if (x < 0) {
lis[i] = 0;
continue;
}
auto it = upper_bound(res.begin(), res.end(), x);
if (it == res.end()) {
res.push_back(x);
lis[i] = res.size();
}
else {
*it = x;
lis[i] = it - res.begin() + 1;
}
}
// Return minimum operations
return N - lis[N - 1];
}
int main()
{
vector<int> A = { -2, 1, 1, 1, 1, -2 };
cout << minOperations(A) << endl;
return 0;
}
import java.util.ArrayList;
import java.util.List;
class Main {
public static int minOperations(int[] A) {
int N = A.length;
// Calculate prefix sums
List<Integer> P = new ArrayList<>();
P.add(A[0]);
for (int i = 1; i < N; i++) {
P.add(A[i] + P.get(i - 1));
}
// If total sum is negative, return -1
if (P.get(N - 1) < 0) {
return -1;
}
// Initialize result and LIS arrays
List<Integer> res = new ArrayList<>();
int[] lis = new int[N];
// Calculate LIS of prefix sum array
for (int i = 0; i < N; i++) {
int x = P.get(i);
if (x < 0) {
lis[i] = 0;
continue;
}
int index = binarySearch(res, x);
if (index < 0) {
res.add(-index - 1, x);
lis[i] = res.size();
} else {
res.set(index, x);
lis[i] = index + 1;
}
}
// Return minimum operations
return N - lis[N - 1] - 1; // Corrected to account for the first negative value
}
// Binary search helper function
public static int binarySearch(List<Integer> arr, int target) {
int low = 0;
int high = arr.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr.get(mid) == target) {
return mid;
} else if (arr.get(mid) < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -(low + 1);
}
// Test case
public static void main(String[] args) {
int[] A = {-2, 1, 1, 1, 1, -2};
System.out.println(minOperations(A)); // Output: 4
}
}
# Function to calculate minimum operations
def min_operations(A):
N = len(A)
# Calculate prefix sums
P = [0] * N
P[0] = A[0]
for i in range(1, N):
P[i] = A[i] + P[i - 1]
# If total sum is negative, return -1
if P[N - 1] < 0:
return -1
# Initialize result and LIS arrays
res = []
lis = [0] * N
# Calculate LIS of prefix sum array
for i in range(N):
x = P[i]
if x < 0:
lis[i] = 0
continue
j = 0
while j < len(res):
if res[j] > x:
break
j += 1
if j == len(res):
res.append(x)
lis[i] = len(res)
else:
res[j] = x
lis[i] = j + 1
# Return minimum operations
return N - lis[N - 1]
# Test case
A = [-2, 1, 1, 1, 1, -2]
print(min_operations(A))
# This code is contributed by rohit singh
using System;
using System.Collections.Generic;
class Program
{
// Function to calculate minimum operations
static int MinOperations(List<int> A)
{
int N = A.Count;
// Calculate prefix sums
List<long> P = new List<long>(N);
P.Add(A[0]);
for (int i = 1; i < N; i++)
{
P.Add(A[i] + P[i - 1]);
}
// If total sum is negative, return -1
if (P[N - 1] < 0)
{
return -1;
}
// Initialize result and LIS arrays
List<long> res = new List<long>();
int[] lis = new int[N];
// Calculate LIS of prefix sum array
for (int i = 0; i < N; i++)
{
long x = P[i];
if (x < 0)
{
lis[i] = 0;
continue;
}
int index = res.BinarySearch(x);
if (index < 0)
{
index = ~index;
res.Insert(index, x);
lis[i] = res.Count;
}
else
{
res[index] = x;
lis[i] = index + 1;
}
}
// Return minimum operations
return N - lis[N - 1] - 1; // Corrected to account for the first negative value
}
static void Main(string[] args)
{
List<int> A = new List<int> { -2, 1, 1, 1, 1, -2 };
Console.WriteLine(MinOperations(A));
}
}
function minOperations(A) {
const N = A.length;
// Calculate prefix sums
const P = [A[0]];
for (let i = 1; i < N; i++) {
P.push(A[i] + P[i - 1]);
}
// If total sum is negative, return -1
if (P[N - 1] < 0) {
return -1;
}
// Initialize result and LIS arrays
const res = [];
const lis = new Array(N).fill(0);
// Calculate LIS of prefix sum array
for (let i = 0; i < N; i++) {
const x = P[i];
if (x < 0) {
lis[i] = 0;
continue;
}
const index = binarySearch(res, x);
if (index < 0) {
res.splice(-index - 1, 0, x);
lis[i] = res.length;
} else {
res[index] = x;
lis[i] = index + 1;
}
}
// Return minimum operations
return N - lis[N - 1] - 1; // Corrected to account for the first negative value
}
// Binary search helper function
function binarySearch(arr, target) {
let low = 0;
let high = arr.length - 1;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -(low + 1);
}
// Test case
const A = [-2, 1, 1, 1, 1, -2];
console.log(minOperations(A));
Output
4
Time Complexity: N*Log(N), As upper_bound() function is used.
Auxiliary Space: O(N), As vectors such as Lis[] and Res[] are used.