Kadane's with a twist - arrays

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

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

Count Number of distinct subarrays

I recently came across this question in one of the coding interviews. The question is as follows:
Given an array A[] of n numbers and a number k, count the total number of distinct subarrays such that each subarray contains at most k odd elements.
1 <= n <= 1000
1 <= A[i] <= 250
1 <= k <= n
I used a DP approach to solve the problem, but my solution does not take care of the distinct part.
public int distinctSubArraysWithAtmostKOddElements(int[] a, int k) {
int l = a.length;
int[][] dp = new int[k + 1][l];
for (int j = 0; j < l; j++) {
dp[0][j] = a[j] % 2 == 0 ? 1 : 0;
}
for (int i = 1; i <= k; i++) {
dp[i][0] = 1;
}
for (int j = 1; j <= k; j++) {
for (int i = 1; i < l; i++) {
if (a[i] % 2 == 0) {
dp[j][i] = Math.max(dp[j - 1][i], 1 + Math.max(dp[j - 1][i - 1], dp[j][i - 1]));
} else {
dp[j][i] = Math.max(dp[j - 1][i], 1 + dp[j - 1][i - 1]);
}
}
}
int tot = 0;
for (int i = 0; i < l; i++) {
tot += dp[k][i];
}
return tot;
}
My solution works in O(nk) time and space.
How can I take care of the distinctness ? Is there a mathematical formula that solves this problem?
Edit:
Eg 1:
A[] = {2,1,2,3} and k = 1
Distinct Subarrays are: {2}, {2,1}, {1}, {1,2}, {2,1,2}, {3}, {2,3}
So answer is 7.
Eg 2:
A[] = {1,1,1} and k = 2
Distinct Subarrays are: {1}, {1,1}
So answer is 2.
Eg 3:
A[] = {1,2,3} and k = 1
Distinct Subarrays are: {1}, {2}, {3}, {1,2}, {2,3}
So answer is 5.
We can iterate over all subarrays and store the hashes of the valid subarrays.The time complexity is O((n^2)*log(n)) and memory complexity O(n^2).
int distinctSubArraysWithAtmostKOddElements(vector<int> a, int k)
{
set<unsigned long long int> hashes;
int prime = 163;
for(int i = 0 ; i < a.size() ; i++)
{
int oddNow = 0;
unsigned long long int hashNow = 0;
for(int j = i ; j < a.size() ; j++)
{
hashNow = hashNow * prime + a[j];
if( a[j] % 2) oddNow++;
if(oddNow <= k)
hashes.insert(hashNow);
else
break;
}
}
return hashes.size();
}

Minimum contiguous sum that can be obtained by performing at most K swaps

I have this homework:
Given an array consisting of N integers, you are required to print the minimum contiguous sum that can be obtained by performing at most K swaps. During a swap any 2 elements of the given array could be swapped.
I tried this
int currentSum = 0;
int currentMin = 0;
for (int j = 0; j < input.Length; j++)
{
if (input[j] >= 0)
continue;
currentSum += input[j];
if (currentMin > currentSum)
currentMin = currentSum;
}
It will give the minimum sum without any swappings, but how can I improve in no more than K swaps?
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
class TestClass {
static Scanner scanner;
public static void main(String args[] ) throws Exception {
scanner=new Scanner(System.in);
int T=scanner.nextInt();
while(T>0){
int N=scanner.nextInt();
int K=scanner.nextInt();
int[] array=new int[N];
for(int i=0;i<array.length;i++)
{
array[i]=scanner.nextInt();
}
System.out.println(findingMinimumSumSubarray(array, K));
T--;
}
}
public static int findingMinimumSumSubarray(int[] values, int k) {
int N = values.length;
int res = values[0];
for (int L = 0; L < N; L++) {
for (int R = L; R < N; R++) {
List<Integer> A= new ArrayList<Integer>();
List<Integer> B = new ArrayList<Integer>();
int ashu = 0;
for (int i = 0; i < N; i++) {
if (i >= L && i <= R) {
A.add(values[i]);
ashu += values[i];
} else {
B.add(values[i]);
}
}
Collections.sort(A);
Collections.sort(B);
Collections.reverse(B);
res = Math.min(res, ashu);
for (int t = 1; t <= k; t++) {
if (t > A.size() || t > B.size()) break;
ashu -= A.get(A.size() - t);
ashu += B.get(B.size() - t);
res = Math.min(res, ashu);
}
}
}
return res;
}
}
You solution is not correct even without swap.
Test: [-1, 2, -1]. Your answer on this test is -2. Correct answer: -1
I hope that my solution is not best and there is better approach.
Simple O(N^3) complexity solution.
Let's assume that our final minimum contiguous segment will be [L, R] for some 0 <= L <= R < N. Now we have two multiset: A and B. A - multiset with "inner" numbers (numbers that are inside range [L, R]) and B - multiset with "outer" numbers (numbers that are outside of range [L, R]). Out goal is to minimize sum of numbers in A - sum(A). Making swap inside A or B is meaningful, because it will not affect to sum(A). We can swap one element from A with other element in B. We have no more than K swaps, and it means that no more than K elements in A will be swapped with no more than K elements in B. To reach minimum value of sum(A) we will take some maximum elements in A and swap them with minimum elements in B. For example:
A = {-3, -3, -1, 2}; B = {-4, 1, 3, 6}; K = 2;
We can make 0 swaps, A = {-3, -3, -1, 2}; B = {-4, 1, 3, 6}; then sum(A) == -3
We can make 1 swaps, A = {-3, -3, -1, -4}; B = {2, 1, 3, 6}; then sum(A) == -11
We can make 2 swaps, A = {-3, -3, 1, -4}; B = {2, -1, 3, 6}; then sum(A) == -9
Answer is sum(A) == -11
For range [L, R] we can get minimum possible sum. To obtain answer for our initial problem we will iterate over all possible ranges [L, R]. 0 <= L <= R < N
Naive implementation. O(N^3logn) complexity.
int get_minimum_contiguous_sum(vector <int> values, int k) {
int N = values.size();
int ans = values[0]; // initializing with any possible sums
for (int L = 0; L < N; L++) {
for (int R = L; R < N; R++) {
vector <int> A, B; // our "inner" and "outer" sets
int suma = 0; // will store initial sum of elements in A
for (int i = 0; i < N; i++) {
if (i >= L && i <= R) {
A.push_back(values[i]);
suma += values[i];
} else {
B.push_back(values[i]);
}
}
// Sorting set A in non-descending order
sort(A.begin(), A.end());
// Sorting set B in non-increasing order
sort(B.begin(), B.end());
reverse(B.begin(), B.end());
ans = min(ans, suma); // Updating answer with initial state
// Iterating number of swaps that we will make
for (int t = 1; t <= k; t++) {
// if some of two sets contain less than t elements
// then we cannot provide this number of swaps
if (t > A.size() || t > B.size()) break;
// Swapping t-th maximum of A with t-th minimum of B
// It means that t-th maximum of A subtracts from suma
// and t-th minimum of B added to suma
suma -= A[A.size() - t];
suma += B[B.size() - t];
ans = min(ans, suma);
}
}
}
return ans;
}
Optimization
Let's assume that for the range [L, R] we already know sorted set A and reverse sorted set B. When we will compute for the range [L, R + 1] exactly one element will be deleted from B and inserted in A(this number is exactly values[R+1]). C++ has containers set and multiset that can allow us to insert and remove in O(log) time and iterate in O(n) time. Other programming languages also has same containers (in java it is TreeSet/SortedSet). So when we move R to R+1, we will make some simple queries to multiset(insert/remove).
O(N^3) solution.
int get_minimum_contiguous_sum(vector <int> values, int k) {
int N = values.size();
int ans = values[0]; // initializing with any possible sums
for (int L = 0; L < N; L++) {
// "inner" multiset
// Stores in non-increasing order to iterate from beginning
multiset<int, greater<int> > A;
// "outer" multiset
// multiset by defaul stres in non-decreasing order
multiset<int> B;
// Initially all elements of array in B
for (int i = 0; i < N; i++) {
B.insert(values[i]);
}
int suma = 0; // Empty set has sum=0
for (int R = L; R < N; R++) {// Iterate over all possible R
// Removing element from B and inserting to A
B.erase(B.find(values[R]));
A.insert(values[R]);
suma += values[R];
ans = min(ans, suma);
__typeof(A.begin()) it_a = A.begin();
__typeof(B.begin()) it_b = B.begin();
int cur = suma;
for (int i = 1; i <= k; i++) {
if (it_a != A.end() && it_b != B.end())
break;
cur -= *it_a;
cur += *it_b;
ans = min(ans, cur);
it_a++;
it_b++;
}
}
}
return ans;
}

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;
}
}

Variation of a Maximum_Subarray_Problem

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]);
}

Resources