5

Let's assume we've got a sequence of integers of given length n. We want to delete some elements (maybe none), so that the sequence is increasing and decreasing by turns in result. It means, that every element should have neighbouring elements either both bigger or both smaller than itself. For example 1 3 2 7 6 and 5 1 4 2 10 are both sequences increasing and decreasing by turns. We want to delete some elements to transform our sequence that way, but we also want to maximize the sum of elements left. So, for example, from sequence 2 18 6 7 8 2 10 we want to delete 6 and make it 2 18 7 8 2 10.

I am looking for an effective solution to that problem. Example above shows that the most naive greedy algorithm (delete every first element that breaks the sequence) won't work - it would delete 7 instead of 6, which would not maximize the sum of elements left. Any ideas how to solve that effectively (O(n) or O(n log n) probably) and correctly?

DAle
  • 8,990
  • 2
  • 26
  • 45
piternet
  • 315
  • 1
  • 5
  • 2
    So in "1 3 2 7 6" the number 3 has a bigger and a smaller neighbour? You rexamples look like you mean "either both bigger or both smaller". – Yunnosch Nov 18 '17 at 22:05

1 Answers1

0

For every element of the sequence with index i we will calculate F(i, high) and F(i, low), where F(i, high) equals to the biggest sum of the subsequence with wanted characteristics that ends with the i-th element and this element is a "high peak". (I'll explain mainly the "high" part, the "low" part can be done similarly). We can calculate these functions using the following relations:

enter image description here

The answer is maximal among all F(i, high) and F(i, low) values.

That gives us a rather simple dynamic programming solution with O(n^2) time complexity. But we can go further.

We can optimize a calculation of max(F(j,low)) part. What we need to do is to find the biggest value among previously calculated F(j, low) with the condition that a[j] < a[i]. This can be done with segment trees.

First of all, we'll "squeeze" our initial sequence. We need the real value of the element a[i] only when calculating the sum. But we need only the relative order of the elements when checking that a[j] is less than a[i]. So we'll map every element to its index in the sorted elements array without duplicates. For example, sequence a = 2 18 6 7 8 2 10 will be translated to b = 0 5 1 2 3 0 4. This can be done in O(n*log(n)).

The biggest element of b will be less than n, as a result, we can build a segment tree on the segment [0, n] with every node containing the biggest sum within the segment (we need two segment trees for "high" and "low" part accordingly). Now let's describe the step i of the algorithm:

  1. Find the biggest sum max_low on the segment [0, b[i]-1] using the "low" segment tree (initially all nodes of the tree contain zero).
  2. F(i, high) is equal to max_low + a[i].
  3. Find the biggest sum max_high on the segment [b[i]+1, n] using the "high" segment tree.
  4. F(i, low) is equal to max_high + a[i].
  5. Update the [b[i], b[i]] segment of the "high" segment tree with F(i, high) value recalculating maximums of the parent nodes (and [b[i], b[i]] node itself).
  6. Do the same for "low" segment tree and F(i, low).

Complexity analysis: b sequence calculation is O(n*log(n)). Segment tree max/update operations have O(log(n)) complexity and there are O(n) of them. The overall complexity of this algorithm is O(n*log(n)).

DAle
  • 8,990
  • 2
  • 26
  • 45