6
votes

I stumbled upon this problem on Codility Lessons, here is the description:

A non-empty zero-indexed array A consisting of N integers is given.

A triplet (X, Y, Z), such that 0 ≤ X < Y < Z < N, is called a double slice.

The sum of double slice (X, Y, Z) is the total of A[X + 1] + A[X + 2] + ... + A[Y − 1] + A[Y + 1] + A[Y + 2] + ... + A[Z − 1].

For example, array A such that:

A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2

contains the following example double slices:

double slice (0, 3, 6), sum is 2 + 6 + 4 + 5 = 17,

double slice (0, 3, 7), sum is 2 + 6 + 4 + 5 − 1 = 16,

double slice (3, 4, 5), sum is 0.

The goal is to find the maximal sum of any double slice.

Write a function:

int solution(vector &A);

that, given a non-empty zero-indexed array A consisting of N integers, returns the maximal sum of any double slice.

For example, given:

A[0] = 3
A[1] = 2
A[2] = 6
A[3] = -1
A[4] = 4
A[5] = 5
A[6] = -1
A[7] = 2

the function should return 17, because no double slice of array A has a sum of greater than 17.

Assume that:

N is an integer within the range [3..100,000]; each element of array A is an integer within the range [−10,000..10,000].

Complexity:

expected worst-case time complexity is O(N); expected worst-case space complexity is O(N), beyond input storage (not counting >the storage required for input arguments).

Elements of input arrays can be modified.

I have already read about the algorithm with counting MaxSum starting at index i and ending at index i, but I don't know why my approach sometimes gives bad results. The idea is to compute MaxSum ending at index i, ommiting the minimum value at range 0..i. And here is my code:

int solution(vector<int> &A) {
    int n = A.size();

    int end = 2;   

    int ret = 0;
    int sum = 0;

    int min = A[1];

    while (end < n-1)
    {
        if (A[end] < min)
        {
            sum = max(0, sum + min);
            ret = max(ret, sum);
            min = A[end];
            ++end;
            continue;
        }
        sum = max(0, sum + A[end]);
        ret = max(ret, sum);
        ++end;
    }

    return ret;
}

I would be glad if you could help me point out the loophole!

4
Isn't 4 the sum of (3,4,5) in your examples?Tarc
@Tarc no because you have to omit A[x], A[y] and A[z]. So you can't sum up anything there.RockOnRockOut

4 Answers

11
votes

My solution based on bidirectional Kadane's algorithm. More details on my blog here. Scores 100/100.

public int solution(int[] A) {
  int N = A.length;
  int[] K1 = new int[N];
  int[] K2 = new int[N];

  for(int i = 1; i < N-1; i++){
    K1[i] = Math.max(K1[i-1] + A[i], 0);
  }
  for(int i = N-2; i > 0; i--){
    K2[i] = Math.max(K2[i+1]+A[i], 0);
  }

  int max = 0;

  for(int i = 1; i < N-1; i++){
    max = Math.max(max, K1[i-1]+K2[i+1]);
  }

  return max;
}
2
votes

Here is my code:

int get_max_sum(const vector<int>& a) {
    int n = a.size();
    vector<int> best_pref(n);
    vector<int> best_suf(n);
    //Compute the best sum among all x values assuming that y = i.
    int min_pref = 0;
    int cur_pref = 0;
    for (int i = 1; i < n - 1; i++) {
        best_pref[i] = max(0, cur_pref - min_pref);
        cur_pref += a[i];
        min_pref = min(min_pref, cur_pref);
    }
    //Compute the best sum among all z values assuming that y = i.
    int min_suf = 0;
    int cur_suf = 0;
    for (int i = n - 2; i > 0; i--) {
        best_suf[i] = max(0, cur_suf - min_suf);
        cur_suf += a[i];
        min_suf = min(min_suf, cur_suf);
    }
    //Check all y values(y = i) and return the answer.
    int res = 0;
    for (int i = 1; i < n - 1; i++)
        res = max(res, best_pref[i] + best_suf[i]);
    return res;
 }

 int get_max_sum_dummy(const vector<int>& a) {
    //Try all possible values of x, y and z.
    int res = 0;
    int n = a.size();
    for (int x = 0; x < n; x++)
        for (int y = x + 1; y < n; y++)
            for (int z = y + 1; z < n; z++) {
                int cur = 0;
                for (int i = x + 1; i < z; i++)
                    if (i != y)
                        cur += a[i];
                res = max(res, cur);
            }
    return res;
 }

bool test() {
    //Generate a lot of small test cases and compare the output of 
    //a brute force and the actual solution.
    bool ok = true;
    for (int test = 0; test < 10000; test++) {
        int size = rand() % 20 + 3;
        vector<int> a(size);
        for (int i = 0; i < size; i++)
            a[i] = rand() % 20 - 10;
        if (get_max_sum(a) != get_max_sum_dummy(a))
            ok = false;
    }
    for (int test = 0; test < 10000; test++) {
        int size = rand() % 20 + 3;
        vector<int> a(size);
        for (int i = 0; i < size; i++)
            a[i] = rand() % 20;
        if (get_max_sum(a) != get_max_sum_dummy(a))
            ok = false;
    }
    return ok;
}

The actual solution is get_max_sum function(the other two are a brute force solution and a tester functions that generates a random array and compares the output of a brute force and actual solution, I used them for testing purposes only).

The idea behind my solution is to compute the maximum sum in a sub array that that starts somewhere before i and ends in i - 1, then do the same thing for suffices(best_pref[i] and best_suf[i], respectively). After that I just iterate over all i and return the best value of best_pref[i] + best_suf[i]. It works correctly because best_pref[y] finds the best x for a fixed y, best_suf[y] finds the best z for a fixed y and all possible values of y are checked.

1
votes
def solution(A):
    n = len(A)
    K1 = [0] * n
    K2 = [0] * n
    for i in range(1,n-1,1):
        K1[i] = max(K1[i-1] + A[i], 0)

    for i in range(n-2,0,-1):
        K2[i] = max(K2[i+1]+A[i], 0)

    maximum = 0;
    for i in range(1,n-1,1):
        maximum = max(maximum, K1[i-1]+K2[i+1])

    return maximum

def main():
    A = [3,2,6,-1,4,5,-1,2]
    print(solution(A))

if __name__ == '__main__': main()
0
votes

Ruby 100%

def solution(a)
  max_starting =(a.length - 2).downto(0).each.inject([[],0]) do |(acc,max), i|
    [acc, acc[i]= [0, a[i] + max].max ]
  end.first

  max_ending =1.upto(a.length - 3).each.inject([[],0]) do |(acc,max), i|
    [acc, acc[i]= [0, a[i] + max].max ]
  end.first

  max_ending.each_with_index.inject(0) do |acc, (el,i)|
    [acc, el.to_i + max_starting[i+2].to_i].max
  end
end