Variation of a Maximum_Subarray_Problem - arrays

This is a variation of a Maximum_subarray_problem.
Find contiguous subarray of length at most K, in an array of length N ( 0 <= K <= N )
Eg. given [-13,-1,1,1,2,3,1,1] and K = 2, maximum K-subarray sum is 5
Looking for O(N) solution. The trivial solution is O(N*N), checking range between each pair. I feel it can be improved to O(N).

Let your array be indexed with 1 to n. Let f(i) be the maximum subarray that ends at i and prefixSum(i) be the prefix sum up to (and including) index i. Then we have
f(i) = prefixSum(i) - MIN(j = i - K to i - 1, prefixSum(j))
f(i) can be computed in linear time by using a sliding window minimum data structure. Here's another implementation of the queue, it support enqueue, dequeue and find-max/min. Using that queue as a primitive, the algorithm would look like this in pseudocode:
global_max = -infinity
prefixSum[0] = 0
q = new MinQueue()
for i := 1 to n:
prefixSum[i] = prefixSum[i - 1] + a[i]
if i > 1
q.enqueue(prefixSum[i - 1])
if i - K - 1 >= 1
q.dequeue()
global_max = max(global_max, prefixSum[i] - q.min())

This is Java code that performing the same task in O(n)
public void maxSubSet (int[] array, int k)
{
int startIndex = -1;
int max = 0;
for(int i =0; i<k; i++){ //Assuming k<array.length
max += array[i];
startIndex = 0;
}
for(int i=k; i<array.length; i++){
int sum = array[i] - array[i-k];
if (sum > max){
max = sum;
startIndex = i;
}
}
System.out.println("Max Sum:" + max);
for(int i = startIndex; i<startIndex+k; i++)
System.out.println(i+":"+array[i]);
}

Related

Finding the max sum of n consecutive numbers where sum < k

I am given an array and asked to find the maximum possible sum of n consecutive numbers, where the maximum sum is less than a given value k.
For example:
array = {1, 3, 1, 2, 3, 4, 1}
k = 7
Here, the answer must be 6, because the max sum that we can obtain that is less than 7 is: arr[1] + arr[2] + arr[3] = 3 + 1 + 2 = 6
How can I write an algorithm to find such a value?
(I have done it with a nested for loop, but it takes too much time, is there any other way to make this program work?)
Basic Foundation
First off, I'd suggest you read up a bit more about time complexity. There are enough good resources out there, and Complexity Theory is one of them. This should help you understand why your solution is not fast enough.
O(n^3) Solution
A brute-force approach is to check all possible subarrays, by iterating over the start and end points of the subarray, and adding up all elements in between.
Given an array arr of size n, the way to do that would be as follows:
for (int l = 0; l < n; ++l) {
for (int r = l; r < n; ++r) {
long sum = 0;
for (int pos = l; pos <= r; ++pos) {
sum += arr[pos];
}
if (sum < k)
max = Math.max(max, sum);
}
}
The final answer is stored in max.
O(n^2) Solution
A faster solution would eliminate the third loop by making use of prefix sums. This can be done as follows, with the help of an auxiliary array preSum of the same size as the main array, where preSum[i] stores the sum of the first i elements:
preSum[0] = arr[0];
for (int i = 1; i < n; ++i)
preSum[i] = preSum[i - 1] + arr[i];
for (int l = 0; l < n; ++l) {
for (int r = l; r < n; ++r) {
long sum = preSum[r];
if (l > 0)
sum -= preSum[l - 1];
if (sum < k)
max = Math.max(max, sum);
}
}
O(n) solution
The most efficient solution to this problem uses a sliding window/two-pointer approach. Note that we assume that negative numbers are not allowed.
We start with both l and r at the beginning of the array. There are two possible cases at every stage:
Sum of the current subarray <k: We can be hopeful and try to add more elements to the subarray. We do this by moving r one step further to the right.
Sum of the current subarray >=k: We need to remove some elements to make the sum satisfy the given constraint. This can be done by moving l one step to the right.
This is repeated till we hit we need to increment r, but have reached the end of the array. The code looks something like this:
long max = 0;
int l = 0;
int r = 0;
long sum = arr[0];
while (true) {
if (sum >= k) {
sum -= arr[l];
++l;
} else {
if (r == n - 1)
break;
else {
++r;
sum += arr[r];
}
}
if (sum < k)
max = Math.max(max, sum);
}

How to find number of comparisons and complexity of a given algorithm

I am trying to determine the total number of comparisons (without counting the loops’ stopping conditions) and how the complexity grows with N.
1.
for (int ind = 0; ind < N; ind++)
{
if (arr[ind] < 0)
arr[ind] = arr[ind] * 2;
}
2.
bool there_are_duplicates = false;
for (int ind1 = 0; ind1 < N; ind1++)
{
for (int ind2 = 0; ind2 < N; ind2++)
if (ind1 != ind2 && arr[ind1] == arr[ind2])
there_are_duplicates = true;
}
3.
for (int ind = 0; ind < N; ind++)
{
int sum = 0;
int nb = 0;
for (int n = 0; n < 4; n++)
{
if (ind >= n)
{
sum = sum + arr[ind - n];
nb++;
}
}
arr[ind] = sum / nb;
}
For the first one, complexity is N and number of comparisons is also N or its just equal to one?
For the second one, complexity is N^2 and number of comparisons is 2(N^2) or its just equal to two?
For the second one, complexity is N and number of comparisons is also 4N or its just equal to one or 4?
Let's count:
You compare for each item of the array, so you have N comparisons and O(N) complexity
You have nested loop, you compare every item (N of them) with all the others but theirselves (i.e. with N - 1 items). You have N * (N - 1) comparisons and O(N^2) complexity
Here, you process each item 4 times. So, you have 4 * N comparisons and O(N) complexity

Find minimum no. of fountains needed

There is a linear garden from 1 to n. At each point there is a fountain. Given array a[n]tells info about fountain such that its range is max(i-a[i],1) to the left of fountain to min(i+a[i],n) to the right of fountain. Find minimum no. of fountains needed to be activated so that whole garden is covered.
e.g.if n=3 and a={1,2,1} then second fountain has range 1 to 3. So only 1 fountain needed. Here fountain at 1 has range of 1 to 2,
fountain at 2 has range of 1 to 3 and fountain at 3 has range of 2 to 3
So only fountain 2 is enough to be activated to cover the whole garden.
Traverse through all the fountains and construct array jumps:
jumps[i] = {max range of all the fountains having their range's left boundary at i}
for (int i = 1; i <= n; i++){
l = max(1, i - arr[i-1])
r = min(n, i + arr[i-1])
jumps[l] = max(jumps[l], r - l);
}
Then apply Jump Game II (https://leetcode.com/problems/jump-game-ii/) on jumps array.
This takes O(n) time
I think you are looking for this solution off O(n) time and O(n) space.
Create interval array to store the right most range which can be covered from the current index.
public int findMinActivatedFountain(int[] arr, int n) {
int[] interval = new int[n];
int count = 1;
// At each index we'll store the max right possible from this index.
for (int i = 1; i <= n; i++) {
int left = Math.max(i - arr[i - 1], 1);
int right = Math.min(i + arr[i - 1], n);
interval[left - 1] = right;
}
int right = interval[0];
int currMax = right;
for (int i = 1; i < n; i++) {
currMax = Math.max(currMax, interval[i]);
// If the last fountain can cover only this point, then update with next fountain.
if (i == right) {
count++;
right = currMax;
}
}
return count;
}
Ravi's answer is correct, but there's a little bug in that
// At each index we'll store the max right possible from this index.
for (int i = 1; i <= n; i++) {
int left = Math.max(i - arr[i - 1], 1);
int right = Math.min(i + arr[i - 1], n);
interval[left - 1] = right;
}
Here it should be interval[left-1] = max(interval[left-1],right),
We should store the max of all ranges with that point being the starting point. Rest all is correct.
Assign most powerful fountain so that it covers 1 to 2 * range -1
Remove assigned fountain and 1 to 2 * range - 1 from garden
recurse
Time complexity- O(n)
store the lower and upper limits of the range in an array (fountainArray below)
take a stack, to select all the fountains you will need. Initially stack is empty.
rightLimit =0;
leftLimit = 0;
for(i=0; i<n; i++){
if(rightLimit> fountainArray[i].rightLimit){
if(leftLimit<= fountainArray[i].leftLimit){
fountainStack.pop();
//similarly check left limit of the new fountain on top
//keep popping elements till fountainArray[i].leftLimit is > leftLimit of
//fountain on top of stack
}
fountainStack.push(fountainArray[i]);
}
}
return fountainStack.length();
public static int find(int arr[], int n){
for (int i = 1; i <= n; i++){
int l = max(1, i - arr[i-1])
int r = min(n, i + arr[i-1])
jumps[l] = max(jumps[l], r - l);
}
int longest = jump(jumps);
if(arr.lenght == longest){
return 0;
}else{
return arr.length-longest-1;
}
}
// get the minimum jump to reach
public static int jump(int[] A) {
int sc = 0;
int e = 0;
int max = 0;
for(int i=0; i<A.length-1; i++) {
max = Math.max(max, i+A[i]);
if( i == e ) {
sc++;
e = max;
}
}
return sc;
}

Kadane's with a twist

Problem:
Given two arrays A and B, both of size n, find the interval [i,j] (0 <= i,j <= n-1) that maximizes the value of V = sum(A[i:j]) - min(B[i:j]).
Without the array B twist, this problem is just the maximum subarray sum problem, solvable in O(N) with Kadane's algorithm. Now, we have a second array, and we select the minimum element from the range, and deduct it from the sum.
Example:
A = [-5, 2, 3, 4, 5]
B = [-5, 1, 2, 0, -5]
Solution: 19
i=1 to j=4
2+3+4+5 - (-5) = 19
A trivial algorithm is to do a double loop to calculate each (i,j) interval, but this naive approach has O(N^2) time complexity.
I have been trying to find an O(N), or at least an O(NlogN) algorithm, but I haven't been able to achieve it yet.
I would appreciate any ideas on this, thanks!
Edit: The implementation of the solution by Peter for reference:
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int kadane_modified(vector<int>& A, vector<int>& B){
if(A.empty() || B.empty()) return 0;
int size = A.size();
// Backward Kadane's
vector<int> R(size);
int max_so_far = INT_MIN, max_starting_here = 0;
for (int i = size-1; i >= 0; i--)
{
max_starting_here = max_starting_here + A[i];
if (max_so_far < max_starting_here)
max_so_far = max_starting_here;
if (max_starting_here < 0)
max_starting_here = 0;
R[i] = max_starting_here;
}
// Forward Kadane's
vector<int> F(size);
max_so_far = INT_MIN; int max_ending_here = 0;
for (int i = 0; i < size; i++)
{
max_ending_here = max_ending_here + A[i];
if (max_so_far < max_ending_here)
max_so_far = max_ending_here;
if (max_ending_here < 0)
max_ending_here = 0;
F[i] = max_ending_here;
}
// DP that combines previous results
vector<int> V(size);
for(int k = 0; k < size; k++){
if(k < size-1 & k > 0)
V[k] = A[k] + R[k+1] - B[k] + F[k-1];
else if(k == 0)
V[k] = A[k] - B[k] + R[k+1];
else if(k == size-1)
V[k] = A[k] - B[k] + F[k-1];
}
// The maximum V is our answer
int solution = INT_MIN;
for(int i = 0; i < size; i++){
if(solution < V[i]) solution = V[i];
}
return solution;
}
int main()
{
vector<int> A = {-5, 2, 3, 4, 5};
vector<int> B = {-5, 1, 2, 0, -5};
int solution = kadane_modified(A, B);
cout << solution << endl;
return 0;
}
Output:
19
Kadane's algorithm computes the maximum sum of A ending at each position (call this F[i]).
You can also run Kadane's algorithm on the reversed array A to find the maximum sum of A starting at each position (call this R[i]).
We can then use these two arrays to compute the maximum subarray sum A[i:j]-B[k] where i<=k<=j for each position k (by simply computing F[k-1] + A[k] + R[k+1] - B[k] for each k).
This has then solved the slightly different problem of "Find interval i:j which satisfies i<=k<=j and that maximizes A[i:j] - B[k]". However, the highest value that this takes will be the same as choosing B[k] to be the minimum of B[i:j], so this is equivalent to your original problem.
The complexity of this approach is O(n).

Find largest pair sum in array of integers

How can I find the largest pair sum in an array of positive integers of size n, but with the integers at least at a distance k? (For example, if the first element is a[i], then the second element should be a[i+k] (or more).)
I tried this:
int max_sum = 0;
int sum;
for (int i = 0 ; i < n; i++) {
for( int j = i + k; j < n; j++) {
sum = arr_sums[i] + arr_sums[j];
if ( sum > max_sum ) {
max_sum = sum;
}
}
}
but it's too slow for large arrays.
It's quite simple to do in O (n), not O (n²) like your solution.
For each j, 0 ≤ j < n,
calculate m [j] = "largest element from a [j] to a [n - 1]. ".
Obviously m [n - 1] = a [n - 1], m [j] = max (a [j], m [j + 1]).
Then for each i, 0 ≤ i < n - k, calculate a [i] + m [i + k],
and pick the largest of these.
It should be obvious how to do this without actually storing the values m [j] except for one.
//assuming we checked first for n<=k
int max_lagged = arr_sums[0];
int max_sum = max_lagged+arr_sums[k];
int sum;
for (int i = k+1 ; i < n; i++) {
if (arr_sums[i-k] > max_lagged) {
max_lagged=arr_sums[i-k];
}
sum = arr_sums[i] + max_lagged;
if ( sum > max_sum ) {
max_sum = sum;
}
}

Resources