2.1 Most valuable subinterval
We are given an integer array like this:
and are interested in subintervals. For example, in python notation,
is a subinterval of
The value of an interval is the sum of its entries. So
Problem 2.1.1 Given an array of integers as in the example above, compute the maximum value of a subinterval (this includes the possibility of the empty subinterval, so the maximum value is always at least 0).
Input: A single line containing
Output: A single line containing a single integer, the maximum value of a subinterval.
Sample input:
-8 5 -4 7 -9 3 0 7 -5 8 -1 2 -5 2 9 -3
Sample output:
20
Test cases
Before starting to code, you should think of a handful of test cases (unless this is during an actual competition). Write them into a file and use them to test your algorithm later on.
Exercise 2.1.2 Write test cases for this problem. In particular,
- the above example;
- another hand-crafted example;
- edge cases: an array with only positive numbers; an array with only negative numbers; an empty array; other edge cases you can come up with.
Create a sub-directory testcases. For each test case compute the answer by hand and write it into a file. Produce a pair file like all-negative.in and all-negative.out.
Exercise 2.1.3 Write a program that generates random instances. Here is mine in action:
python generate-random-test-cases.py 10 -20 20
-9 18 19 -14 2 -10 12 -16 14 18
From here, we will proceed as follows: we will first write a pretty simple
but inefficient solution (in fact, of running time
python cubic-but-wrong.py < testcases/example-in-lecture-notes.in
20
Using the command line program diff, we can compare it to the (hopefully) correct output, stored in example-in-lecture-notes.out:
python cubic-but-wrong.py < testcases/example-in-lecture-notes.in | diff -b - testcases/example-in-lecture-notes.out
python cubic-but-wrong.py < testcases/all-positive.in | diff -b - testcases/all-positive.out
1c1 < 4 --- > 5
diff file1.txt file2.txt
Here, one input to be compared is the output of our program. We could of course store it in some temporary file and then compare it:
python cubic-but-wrong.py < testcases/example-in-lecture-notes.in >. temp.out
diff -b temp.out
I wrote cubic-but-wrong.py to read from stdin (the console user input). So instead of manually
We see that our wrong implementation succeeds on the sample input -8 5 -4 7 -9 3 0 7 -5 8 -1 2 -5 2 9 -3 that we have seen above but fails on all-positive.in, which is 1 1 1 1 1. The command diff shows us the difference: its first input (stdin as specified by -) has 4, but the second input, testcases/all-positive.out, has a 5.
The trivial solution
The trivial solution is the iterate over all pairs
Exercise 2.1.4 What is the running time of the algorithm just described?
Here is my implementation of the trivial algorithm, which you can also find in cubic-but-wrong.py.
def maximum_sum(array):
n = len(array)
max_so_far = 0
for i in range(0,n):
for j in range(i+1,n):
array_here = array[i:j]
sum_here = sum(array[i:j])
# print("array[", i, ":", j, "] = ", array_here, ", sum is ", sum_here)
max_so_far = max(max_so_far, sum(array[i:j]))
return max_so_far
array = list(map(int, input().split()))
print(maximum_sum(array))
Exercise 2.1.5 Find the bug in my code, correct it, and test it.
Pitfalls
When working with arrays, an eternal pitfall is indexing and one-off-errors: do we include
the right boundary or don't we? In python, array[i:j] includes index
The linear algorithm: Dynamic Programming
As usual in dynamic programming, we have to think about what partial results we want to compute
and
store.
For an index
, the value of the best subinterval of . , the value of the best subinterval of the form
Savor the difference: for
Initialization. For
Update. Suppose we have computed
The last line is a contradiction because we assumed
The update rule for
Problem 2.1.6 Implement the linear time dynamic programming algorithm just described.
More Information
We don't only want to compute the value of the best subinterval. We want to know the best subinterval itself. So our algorithm should output three things: left, right, and value. Since ICPC-style problems are mostly judged by running them on test inputs and comparing the output to the correct test outputs, we run into difficulties if answers are not unique. Thus, we introduce the following tie breaking rules:
- If two intervals have the same value, the smaller (shorter) interval wins.
- If two intervals have the same value and the same length, then the one more to the left
wins.
That is, if
and have the same value (and both have length ) and , then wins, i.e., is considered better.
Under these tie-breaking rules, every array has a unique optimal interval
Problem 2.1.7
Adapt the cubic and the linear algorithm such that they output three integers
Sample input:
6 5 2 -20 9 4
Sample output:
(4, 6, 13)
More test cases
most-valuable-interval-testcases.zip
I didn't provide correct answers for those test cases. You can check the smaller ones against the cubic algorithm, which is hopefully correct.