At any moment during the cutting, we have a set of independent sub-problems — blocks of chocolate. If we find the optimal solution for each of the blocks, together we get the optimal solution for the whole chocolate. This clearly hints at a dynamic programming solution.
Each sub-problem we may encounter corresponds to a rectangular part of the chocolate, and it can be described by four coordinates: specifically, two
and two
coordinates — the coordinates of its upper left and lower right corner. Hence we have
sub-problems to solve.
Now to solve a given sub-problem, we have to try all possible cuts. There are
possible cuts to try — at most
horizontal and
vertical ones. Each possible cut gives us two new, smaller sub-problems we solve recursively. Obviously, the recursion stops as soon as we reach a
block.
Assume that someone has given us a function
that returns the number of raisins in the rectangle given by coordinates
and
in constant time.
Using this function we can solve the entire problem in
. We will use recursion with memoization. Given any of the
sub-problems, first check the memoization table to see whether we have computed it already. If yes, simply return the previously computed value. Otherwise, proceed as follows: The cost of the first cut is
, which we have supposed can be computed in
time. For each possible placement of the first cut, recursively determine the cost of the remaining cuts in each sub-problem, and pick the optimal choice, storing the answer in the memoization table.

We are only missing one piece of the puzzle: the function
. All possible values can easily be precomputed in
and stored in an array.
Alternatively, we can use two-dimensional prefix sums: let
be the input array, and let
. The values
are called two-dimensional prefix sums. They can be computed using the formula:

Having the two-dimensional prefix sums, we can compute the sum in any rectangle, using a similar formula. The sum in the rectangle with corners
and
is:

Comments