Search code examples
arraysalgorithmdata-structuresdynamic-programmingkadanes-algorithm

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

Solution

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