Find the sum of maximum difference possible from contiguous subset of a given array.
We are given an array arr[] of n non-negative integers (repeated elements allowed), find out the sum of maximum difference possible from contiguous subsets of the given array.
Suppose max(s) represents the maximum value in any subset ‘s’ whereas min(s) represents the minimum value in the set ‘s’. We need to find the sum of max(s)-min(s) for all possible subsets.
Input : arr[] = {1, 2, 3}
Output : result = 4
Explanation :
All possible subset and for each subset s,
max(s)-min(s) are as :
SUBSET | max(s) | min(s) | max(s)-min(s)
{1, 2} | 2 | 1 | 1
{2, 3} | 3 | 2 | 1
{1, 2, 3} | 3 | 1 | 2
Total Difference sum = 4
Note : max(s) - min(s) for all subset with
single element must be zero.
Constraints:
Array size can be from 1 to 10 power 5, also each element in array can be from 1 to 10 power 5.
This is the code taken from here, but this code checks all possible subsets instead of contiguous subsets:
public static int MOD = 1000000007;
// function for sum of max min difference
public static long maxMin (int arr[], int n)
{
// sort all numbers
Arrays.sort(arr);
// iterate over array and with help of
// horner's rule calc max_sum and min_sum
long min_sum = 0, max_sum = 0;
for (int i = 0; i < n; i++)
{
max_sum = 2 * max_sum + arr[n - 1 - i];
max_sum %= MOD;
min_sum = 2 * min_sum + arr[i];
min_sum %= MOD;
}
return (max_sum - min_sum + MOD)%MOD;
}
So how to get only contiguous subsets and solve this with less time complexity.
You can do this in O(n) time and space.
The technique is to use the algorithm for all nearest smaller values. First, break the problem into two parts:
Find the sum of all subarray maximums
Find the sum of all subarray minimums, and subtract this from the first sum.
The solution for both problems is identical apart from exchanging all occurrences of 'less than' with 'greater than', so I'll describe the minimums case only.
For each element A[i] of the array, you can ask: 'How many subarrays have A[i] as their minimum element?' To deal with duplicates, assume we always take the rightmost occurrence of a minimum element within a subarray as the 'representative' element.
The question transforms to finding how far to the left of A[i] we can go before seeing an element strictly smaller than A[i], and how far to the right of A[i] we can go before seeing an element as small as A[i]. Multiply these two distances to get all possible choices of left and right endpoints among subarrays that have A[i] as their minimum element. We can find both of these directly with the 'all nearest smaller values' algorithm, and solve the rest of the problem like so (pseudocode):
1. For each position i in the array A, let previous_smaller[i]
be the largest index j such that A[j] < A[i] and 0 <= j < i,
or -1 if there is no such index.
2. For each position i in the array A, let next_smaller_or_equal[i]
be the smallest index j such that A[j] <= A[i] and i < j < n,
or n if there is no such index.
3. Return the sum over all i, 0 <= i < n, of
(A[i] *
(next_smaller_or_equal[i] - i) *
(i - previous_smaller[i]))
There are several implementations of all nearest smaller values in the answers to this question, for example, and pseudocode in the Wikipedia article. To find 'next smaller values' instead of 'previous smaller values', simply run the algorithm on a reversed array A (or just traverse A in reverse order, from A[n-1] down to A[0]).
Sample implementation of the whole algorithm (in Python):
def max_difference_sum(A: List[int]) -> int:
"""Given an array of integers A, compute the
sum over all subarrays B of max(B) - min(B)
by using nearest smaller values"""
n = len(A)
# Convention to take the rightmost min or max in each subarray.
previous_smaller = list(range(n))
next_smaller_or_equal = list(range(n))
previous_larger = list(range(n))
next_larger_or_equal = list(range(n))
# Compute the previous larger and smaller in a single loop.
for i in range(n):
j = i - 1
while j >= 0 and A[j] >= A[i]:
j = previous_smaller[j]
previous_smaller[i] = j
j = i - 1
while j >= 0 and A[j] <= A[i]:
j = previous_larger[j]
previous_larger[i] = j
for i in reversed(range(n)):
j = i + 1
while j < n and A[j] > A[i]:
j = next_smaller_or_equal[j]
next_smaller_or_equal[i] = j
j = i + 1
while j < n and A[j] < A[i]:
j = next_larger_or_equal[j]
next_larger_or_equal[i] = j
max_sums = sum(A[i]
* (next_larger_or_equal[i] - i)
* (i - previous_larger[i])
for i in range(n))
min_sums = sum(A[i]
* (next_smaller_or_equal[i] - i)
* (i - previous_smaller[i])
for i in range(n))
return max_sums - min_sums
Let's use the induction method.
Assume we solved the problem somehow for the array of size N and know the desired sum.
Let's find the solution if the element A[n+1] is added.
We need to calculate the sums only for all sequences that include A[n+1].
A[0], A[1], A[2], ..., A[n+1]
A[1], A[2], ..., A[n+1]
...
A[n], A[n+1]
All other contiguous subsets were calculated at the previous step somehow.
In order to calculate their minimums and maximums let's examine them in the reverse order.
A[n+1], A[n]
A[n+1], A[n], A[n-1]
...
A[n+1], A[n], ..., A[0]
This gives us an ability to iterate them and find their extremes in a single loop.
So the code is
int a[] = {1, 2, 3};
long sum = 0;
for (int i = 1; i < a.length; i++) {
int min = a[i];
int max = a[i];
for (int j = i - 1; j >= 0; j--) {
int current = a[j];
if (current < min) min = current;
if (current > max) max = current;
sum += max - min;
}
}
System.out.println("Sum = " + sum);
The solution complexity is O(n^2) as there are two nested loops.
You can achieve this using stream:
public static int difference(int[] arr) {
int size = arr.length;
return IntStream.range(0, size)
.flatMap(i -> IntStream.range(i + 1, size)
.mapToObj(j -> Arrays.stream(arr, i, j + 1).summaryStatistics())
.mapToInt(stat -> stat.getMax() - stat.getMin()))
.sum();
}
Alternatively, as noticed by #kcsquared, you can use 2 stream, one for max sum and the other for min sum, and subtract them. This approach also avoids unnecessary boxing and unboxing.
public static int difference2(int[] arr) {
int size = arr.length;
int max = IntStream.range(0, size)
.flatMap(i -> IntStream.range(i + 1, size)
.map(j -> Arrays.stream(arr, i, j + 1).max().getAsInt()))
.sum();
int min = IntStream.range(0, size)
.flatMap(i -> IntStream.range(i + 1, size)
.map(j -> Arrays.stream(arr, i, j + 1).min().getAsInt()))
.sum();
return max - min;
}
Since the proposed partial solution is already paying the sort cost, an initial time optimization could pre transform the input arr to a list of (i, arr[i]), then sort by the arr[i]values & in the for loop skip over sorted_tuple_arr with non consecutive i values.
Related
Given an integer array, find the maximum number of sums of adjacent elements that are divisible by n.
Example 1:
input: long[] array = [1, 2, 3], n = 7
output: 0
Example 2:
input: long[] array = [1, 2, 4], n = 7
output: 1
Example 3:
input: long[] array = [2, 1, 2, 1, 1, 2, 1, 2], n = 4
output: 6
Constraints:
array.length = 50000
array[index] <= 2^31 - 1
n <= 2^31 - 1
Currently, this is my code:
public static int maxSums(long[] array, long n) {
int count = 0;
if (array.length == 1 && array[0] == n) {
return 1;
} else {
for (int i = 0; i < array.length; i++) {
long sum = 0;
for (int j = i; j < array.length; j++) {
sum += array[j];
if (sum % n == 0) {
count++;
}
}
}
}
return count;
}
which is essentially the window sliding technique. However, this code runs with time complexity O(n^2) which is pretty slow, and results in Apex CPU Time Limit Exceeded towards the higher end of the constraints. Is there a faster way to solve this?
An approach I just thought of is O(n*m), where n is the actual n parameter and m is the array length.
The algorithm remembers for every subsequence up to the current index what reminder the sequence sum has. This information is stored inside the array called currentMod.
When iterating over the input array this currentMod is updated. We simply add to each possible modulo value of iteration i-1 the value of the input array at index i. The updated array includes the number of subsequence sums ending at index i for each possible reminder: 0, 1, 2 up to n-1.
The element first element of tmpMod at index i includes the number of subsequences that end at index i and have a sum divisible by n. Therefore, we add them to our final result.
Here is the algorithm:
public static int maxSums(int[] array, int n) {
int[] currentMod = new int[n];
int count = 0;
for (int i = 0; i < array.length; i++) {
// Add +1 to 0 remainder as a new sequence can start at every index which has sum 0
currentMod[0] += 1;
int[] tmpMod = new int[n];
for (int j = 0; j < currentMod.length; j++) {
// For every subsequence reminder of i-1 calculate the reminders of adding element i to every subsequence
tmpMod[(j + array[i]) % n] += currentMod[j];
}
// Add number of subsequence sums that divide by n with remainder 0 to result
count += tmpMod[0];
currentMod = tmpMod;
}
return count;
}
P.S.: This algorithm is not strictly better/worse than yours. It depends on another input value. This means it depends on your inputs what is more efficient. My algorithm is only better for a case with large arrays and low n values.
EDIT: After a lot of thinking and testing I think I found a good solution. It is O(n) in time complexity. It is also O(n) in space complexity as there can be at most n different remainders with n values in the array.
The algorithm keeps track of the current remainder, which is dividable by the input n from the start. For each new subsequence, we add the 1 at the current remainder. In this way, we already define which total sum (mod n) we need that the subsequence is dividable by n.
public static int maxSums(int[] array, int n) {
Map<Integer, Integer> currentMod = new HashMap<Integer, Integer>();
int count = 0;
int currentZero = 0;
for (int val : array) {
currentMod.put(currentZero, currentMod.getOrDefault(currentZero, 0) + 1);
currentZero = (currentZero + val) % n;
count += currentMod.getOrDefault(currentZero, 0);
}
return count;
}
Also, some comparisons to show that it should work out:
len(array)=50000 and n=1000:
Your method: 11704 ms
My old one: 188 ms
My new one: 13 ms
len(array)=50000 and n=1000000:
Your method: 555 ms
My old one: stopped after 2 minutes
My new one: 6 ms
Given an unsorted array – arr find a pair arr[i] and arr[j] such that
arr[i] < arr[j] & i<j and (arr[i] + arr[j]) is maximum.
Expected time complexity – O(n)
For array a = {4, 1, 3, 2, 5, 3}
pair is (4, 5).
Here is the code I've tried..
void findPair(int[] a){
int n = a.length;
int max = a[0];
int secondMax = Integer.MIN_VALUE;
for(int i=1; i<n; i++){
if(a[i]>max){
secondMax = max;
max = a[i];
}
}
if(secondMax == Integer.MIN_VALUE){
System.out.println("-1 -1");
}
else{
System.out.println(secondMax+" "+max);
}
}
Here's a solution using a stack. The idea is that the stack always contains a descending sequence, such that for each number you look at, it can be paired with the largest number in the stack lower than it.
It's safe to pop the numbers off as you use them, because e.g. if you have a 6 and the top of the stack is 3, there is no need to keep the 3 around in case it can be paired with a bigger number; if there's a 7 later, you would wait to pair it with the 6 instead of the 3.
public void solution(int[] arr) {
Stack<Integer> stack = new Stack<>();
int bestX = -1, bestY = -1, bestSum = -1;
for(int y : arr) {
while(!stack.isEmpty() && stack.peek() < y) {
int x = stack.pop();
if(x + y > bestSum) { bestX = x; bestY = y; bestSum = x + y; }
}
stack.push(y);
}
System.out.println(bestX + " " + bestY);
}
The time complexity is O(n) despite the nested loop, because the inner loop always pops from the stack, and each element is only pushed once so it can only be popped once.
I thought about your question a lot and I think I found an acceptable solution.
You should split the array into subarrays starting from the end of the array (right side). Building the sub arrays is in iterative manner. You start with the right-most number and you add to the subarray all the numbers before him that are lower than him. The iteration goes to the next subarray when you reach a number which is bigger/equal to the right-most number in the subarray.
Example:
Your array is: {1,7,3,4,5,4,6,2}
Output should be: (5,6)
The splitting should be:
{{1, 7}, {3, 4, 5, 4, 6}, {2}}
<--(3) <--(2) <--(1)
You start from the last index with the value of 2. The number before him is 6 so that's the end of the first subarray. Next you start with 6, all the numbers before him until 7 are lower than 6 so you add them to that subarray. The last subarray starts with 7 and adds 1. See arrows for clarification.
Next, check within each subarray which number from left to the right-most number is max and mark it for possible pair with the right-most number.
In our example it would be: (1,7), (5,6).
There are only two options because {2} has only 1 variable. In the subarray with 6 as right-most the max number is 5 and in the subarray with 7 as right-most 1 is the only other number so it is also the max.
If no pair is found, return "no possible pair found".
Finally, check sum and return biggest pair: (5,6)
1+7 = 8 < 5+6 = 11
Why is this O(n) ?
You scan the array once for splitting: O(n)
Each subarray of size d you scan for max value: O(d)
Sum of all subarrays scanning: O(n)
Total: O(n)
I'm not strong in Java so my code can be written in Python and the conversion should be easy (because Python is easy!). If you wish for Python code, let me know and I'll write some for you. Also, I can explain more why this algorithm works (If not fully understood).
DISCLAIMER: Solution is O(n^2), not O(n) as required by the OP
What about using 2 nested loops:
i from 0 to a.length
j from i+1 to a.length
This ensures that i<j
Then have an if to ensure that a[i]<a[j] and find the max
int currentMax = -1;
int foundI = -1;
int foundJ = -1;
for(int i=0; i<a.length; i++) {
for(int j=i+1; j<a.length; j++) {
if(a[i] < a[j] && a[i] + a[j] > currentMax) {
currentMax = a[i] + a[j];
foundI = i;
foundJ = j;
}
}
}
Output:
System.out.println("i:" + foundI);
System.out.println("j:" + foundJ);
System.out.println("a[i]:" + a[foundI]);
System.out.println("a[j]:" + a[foundJ]);
System.out.println("sum:" + currentMax);
i:0
j:4
a[i]:4
a[j]:5
sum:9
That's the interview question that I failed back in the days. Nobody of my friends knows where the mistake is and why I've been told that I failed. That's why I decided to ask you to correct my solution
Given an array of N integers. An integer K divides array into two subarrays.
Left part: A[0], A[1]...A[K];
Right part: A[K+1], A[K+2]... A[N-1];
Need to find the max possible absolute difference of max values in every subarray.
MaxDiff = Math.Abs(Max(A[0], A[1]...A[K]) - Max(A[K+1], A[K+2]... A[N-1]))
Example 1: [1, 3, -3]. If K=1, max difference is |3-(-3)| = 6.
Example 2: [4, 3, 2, 5, 1, 1]. If K=3, max difference is |5 - 1| = 4.
Time and space complexity should be O(n).
As I see space complexity of my solution is not O(n) already..
int getMaxDifference(int[]A){
int [] leftMax = new int [A.length];
int [] rightMax = new int [A.length];
int max1 = Integer.MIN_VALUE;
int max2 = Integer.MIN_VALUE;
int dif = 0;
int maxDif = 0;
for (int i = 0; i< A.length; i++){
if (A[i]>max1) {max1 = A[i];}
leftMax[i] = max1;
}
for (int j = A.length-1; j>0; j--){
if (A[j]>max2) {max2 = A[j];}
rightMax[j] = max2;
}
for (int k = 0; k<A.length; k++){
dif = Math.abs(leftMax[k] - rightMax[k]);
if (dif>maxDif) {maxDif = dif;}}
return maxDif;
}
In your program:
leftMax[k] holds the greatest value in A[0],...,A[k].
rightMax[k] holds the greatest value in A[k],...,A[n-1].
However, the right part should start at index k+1, not at k.
Therefore I suggest you change this part:
for (int k = 0; k<A.length; k++){
dif = Math.abs(leftMax[k] - rightMax[k]);
if (dif>maxDif) {
maxDif = dif;
}
}
to
for (int k = 0; k<A.length - 1; k++){
dif = Math.abs(leftMax[k] - rightMax[k + 1]);
if (dif>maxDif) {
maxDif = dif;
}
}
In other words, the requirement is to compute:
Math.Abs(Max(A[0], A[1]...A[K]) - Max(A[K+1], A[K+2]... A[N-1]))
but I believe your current program computes:
Math.Abs(Max(A[0], A[1]...A[K]) - Max(A[k], A[K+1], A[K+2]... A[N-1]))
The problem is in the Difference Calculation:
If the Input Array is {4,3,2,5,1,1}
Then the Left Array becomes : {4,4,4,5,5,5}
And the Left Array becomes : {5,5,5,5,1,1}
To Calculate the Difference you should compute the difference at kth index of array leftMAX and (k+1)th index of array rightMax .
i.e. for SubArray {4,3,2,5} consider leftMax's subArray {4,4,4,5} and for SubArray {1,1} consider rightMax's subArray {1,1}
i.e. for SubArray {4,3,2,5} and {1,1} the calculation should be between 3rd Index of leftMax and 4th index of rightMax.
Hence Code becomes
for (int k = 0; k<A.length-1; k++){
dif = Math.abs(leftMax[k] - rightMax[k+1]);
if (dif>maxDif) {maxDif = dif;}}
Please note that the rightmost element of leftMax and leftmost element of rightMax doesn't gets included in the calculation.
I'm pretty sure you misinterpreted the question, which was actually "find the maximum absolute difference between any two elements of the 2 arrays".
The answer would require you to find both the max and min elements of each array, then chose the greatest of the absolute of either mina - maxb or maxa - minb.
There is a trivial one-pass O(n) solution that finds both the max and min of each array.
The introduction of K is mostly irrelevant, and possibly a red herring. There are 2 unrelated subarrays specified by an array reference and start and end indices.
I'm studying for a test and found this question:
I can't really determine the complexity, I figured it's either O(n2) or O(n3) and I'm leaning towards O(n3).
Can someone tell me what it is and why?
My idea that it's O(n2) is because in the j loop, j = i which gives a triangle shape, and then the k loop goes from i + 1 to j, which I think is the other half of the triangle.
public static int what(int[] arr)
{
int m = arr[0];
for (int i=0; i<arr.length; i++)
{
for (int j=i; j<arr.length;j++)
{
int s = arr[i];
for (int k=i+1; k<=j; k++)
s += arr[k];
if (s > m)
m = s;
}
}
return m;
}
Also if you can tell me what it does?
I figured it returns the addition of positive integers or the biggest integer in the array.
But for arrays like {99, -3, 0, 1} it returns 99, which I think is because it's buggy. If not than I have no Idea what it does:
{99, 1} => returns 100
{-1, -2, -3} => return -1
{-1, 5, -2} => returns 5
{99, -3, 0, 1} => returns 99 ???
You can proceed methodically, using Sigma Notation, to obtain the order of growth complexity:
You have 3 for statements. For large n, it is quite obvious that is O(n^3). i and j have O(n) each, k is a little shorter, but still O(n).
The algorithm returns the biggest sum of consecutive terms. That's why for the last one it returns 99, even if you have 0 and 1, you also have -3 that will drop your sum to a maximum 97.
PS: Triangle shape means 1 + 2 + ... + n = n(n+1) / 2 = O(n^2)
Code:
for (int i=0; i<arr.length; i++) // Loop A
{
for (int j=i; j<arr.length;j++) // Loop B
{
for (int k=i+1; k<=j; k++) // Loop C
{
// ..
}
}
}
Asymptotic Analysis on Big-O:
Loop A: Time = 1 + 1 + 1 + .. 1 (n times) = n
Loop B+C: Time = 1 + 2 + 3 + .. + m = m(m+1)/2
Time = SUM { m(m+1)/2 | m in (n,0] }
Time < n * (n(n+1)/2) = 1/2 n^2 * (n+1) = 1/2 n^3 + 1/2 n^2
Time ~ O(n^3)
No matter triangle shape or not, it always a complexity O(N^3), but of course with lower constant then a full triple nested cycles.
You can model the running time of the function as
sum(sum(sum(Theta(1), k=i+1..j),j=i..n),i=1..n)
As
sum(sum(sum(1, k=i+1..j),j=i..n),i=1..n) = 1/6 n^3 - 1/6 n,
the running time is Theta(n^3).
If you do not feel well-versed enough in the underlying theory to directly apply #MohamedEnnahdiElIdri's analysis, why not simply start by testing the code?
Note first that the loop boundaries only depend on the array's length, not its content, so regarding the time complexity, it does not matter what the algorithm does. You might as well analyse the time complexity of
public static long countwhat(int length) {
long count = 0;
for (int i = 0; i < length; i++) {
for (int j = i; j < length; j++) {
for (int k = i + 1; k <= j; k++) {
count++;
}
}
}
return count;
}
Looking at this, is it easier to derive a hypothesis? If not, simply test whether the return value is proportional to length squared or length cubed...
public static void main(String[] args) {
for (int l = 1; l <= 10000; l *= 2) {
long count = countwhat(l);
System.out.println("i=" + l + ", #iterations:" + count +
", #it/n²:" + (double) count / l / l +
", #it/n³:" + (double) count / l / l / l);
}
}
... and notice how one value does not approach anyconstant with rising l and the other one does (not incidentally the very same constant associated with the highest power of $n$ in the methodological analysis).
This requires O(n^3) time due to the fact that in the three loops, three distinct variables are incremented. That is, when one inside loop is over, it does not affect the outer loop. The outer loop runs as many times it was to run before the inner loop was entered.
And this is the maximum contiguous subarray sum problem. Self-explanatory when you see the example:
{99, 1} => returns 100
{-1, -2, -3} => return -1
{-1, 5, -2} => returns 5
{99, -3, 0, 1} => returns 99
There is an excellent algorithm known as Kadane's algorithm (do google for it) which solves this in O(n) time.
Here it goes:
Initialize:
max_so_far = 0
max_ending_here = 0
Loop for each element of the array
(a) max_ending_here = max_ending_here + a[i]
(b) if(max_ending_here < 0)
max_ending_here = 0
(c) if(max_so_far < max_ending_here)
max_so_far = max_ending_here
return max_so_far
References: 1, 2, 3.
O(n^3).
You have calculated any two item between arr[0] and arr[arr.length - 1], running by "i" and "j", which means C(n,2), that is n*(n + 1)/2 times calculation.
And the average step between each calculation running by "k" is (0 + arr.length)/2, so the total calculation times is C(n, 2) * arr.length / 2 = n * n *(n + 1) / 4, that is O(n^3).
The complete reasoning is as follows:
Let n be the length of the array.
1) There are three nested loops.
2) The innermost loop performs exactly j-i iterations (k running from i+1 to j inclusive). There is no premature exit from this loop.
3) The middle loop performs exactly n-j iterations (j running from i to n-1 inclusive), each involving j-i innermost iterations, in total (i-i)+(i+1-i)+(i+2-i)+... (n-1-i) = 0+1+2... + (n-1-i). There is no premature exit from this loop.
4) The outermost loop performs exactly n iterations (i running from 0 to n-1 inclusive), each involving 0+1+2+ ... (n-1-i) innermost iterations. In total, (0+1+2... n-1) + (0+1+2+... n-2) + (0+1+2+... n-3) + ... (0). There is no premature exit from this loop.
Now how do handle handle this mess ? You need to know a little about the Faulhaber's formula (http://en.wikipedia.org/wiki/Faulhaber%27s_formula). In a nutshell, it says that the sum of integers up to n is O(n^2); and the sum of the sum of integers up to n is O(n^3), and so on.
If you recall from calculus, the primitive of X is X^2/2; and the primitive of X^2 is X^3/3. Every time the degree increases. This is not by coincidence.
Your code runs in O(n^3).
I'm trying to write a program which solves the maximum subarray problem. I can understand the intuition behind Kadane's Algorithm on a 1-D array as well as the O(N^4) implementation on a 2-D array. However, I am having some trouble understanding the O(N^3) implementation on a 2-D array.
1) Why do we add up the elements with those from the previous rows within the same column?
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= M; j++)
array[i][j] += array[i-1][j];
}
2) I have no understanding of the second part of the algorithm
Tried looking for an explanation on the web but to no avail. Hope to get some help here!
Thanks in advance!
You know how to compute maximum sum sub-array on a 1D array using Kadane's algorithm. Now we want to extend this algorithm for the 2D array. For an O(N^3) algorithm, we have an intuition. If we somehow create N^2 sub problems and then try to run our O(N) Kadane's algorithm, we can solve the maximum sub array problem.
So basically how we create the N^2 sub problems is by iterating over all the top and bottom rows of the matrix. Then we try to find the optimal columns between which the sub array exists by applying kadane's 1D algorithm. We thus sum the numbers between these two rows column wise and then apply kadane's 1D algorithm on this newly formed 1D array.
But we have a problem here. Computing the sums for all the O(n^2) ranges of the top and bottom rows will itself be O(n^4). This bottle neck can be overcome by modifying our matrix by replacing each element with the sum of all the numbers that are above it in that element's column. Thus, now we can find out the sum of numbers between any two rows in O(n) time by subtracting the appropriate arrays in the matrix.
The java pseudo code -
int kadane2D(int array[N][M]){
// Modify the array's elements to now hold the sum
// of all the numbers that are above that element in its column
for (int i = 1; i < N; i++) {
for (int j = 0; j < M; j++){
array[i][j] += array[i-1][j];
}
}
int ans = 0; // Holds the maximum sum matrix found till now
for(int bottom = 0; bottom < N; bottom++){
for(int top = bottom; top < N; top++){
// loop over all the N^2 sub problems
int[] sums = new int[N];
// store the sum of numbers between the two rows
// in the sums array
for(int i = 0; i < M; i++){
if (bottom > 0) {
sums[i] = array[top][i] - array[bottom-1][i];
} else {
sums[i] = array[top][i];
}
}
// O(n) time to run 1D kadane's on this sums array
ans = Math.max(ans, kadane1d(sums));
}
}
return ans;
}
For people who understand the Kadane's 1D algorithm, below should be easy to understand. Basically we try to convert the 2D matrix into 1D by using the prefix sum for each rows. And for each prefix sum row, we just apply the Kadane's 1D algorithm.
Just posting the working Python code:
class Kadane2D:
def maxSumRetangle(self, grid):
def kadane1D(arr):
curmax, maxsofar = 0, float('-inf')
for a in arr:
curmax = max(a, curmax + a)
maxsofar = max(curmax, maxsofar)
return maxsofar
m, n, ans = len(grid), len(grid[0]), float('-inf')
colCum = [[0] * n]
for row in grid:
colCum.append([pre + now for pre, now in zip(colCum[-1], row)])
for top in range(1, m + 1):
for bottom in range(top, m + 1):
sums = [b - t for b, t in zip(colCum[bottom], colCum[top - 1])]
ans = max(ans, kadane1D(sums))
return ans
grid = [[1, 2, - 3], [3, 4, -6]]
assert Kadane2D().maxSumRetangle(grid) == 10
grid = [[1, 2, -1, -4, -20],
[-8, -3, 4, 2, 1],
[3, 8, 10, 1, 3],
[-4, -1, 1, 7, -6]]
assert Kadane2D().maxSumRetangle(grid) == 29
I know it's an old question. But Google doesn't have the right answers, or they're overworked.
No, this is no correct way. Working example, on O(N^2):
/**
* Kadane 1d
* #return max sum
*/
public static int maxSum(int[] a) {
int result = a[0]; //get first value for correct comparison
int sum = a[0];
for (int i = 1; i < a.length; i++) {
sum = Math.max(sum + a[i], a[i]); //first step getting max sum, temporary value
result = Math.max(result, sum);
}
return result;
}
/**
* Kadane 2d
* #param array
* #return max sum
*/
public static int maxSum2D(int array[][]){
int result = Integer.MIN_VALUE; //result max sum
for (int i = 0; i < array.length; i++) {
int sum = maxSum(array[i]);
result = Math.max(result, sum);
}
return result;
}
Fully examples:
Easy: https://pastebin.com/Qu1x0TL8
Supplemented: https://pastebin.com/Tjv602Ad
With indexes: https://pastebin.com/QsgPBfY6