Dynamic Programming Recap

Dynamic Programming, aka DP, “solves the problem by breaking it into
sub-problems and then recursively finding the optimal solutions to the
sub-problems is said to have optimal substructure”(see Wikipedia).
More concretely, the problem suffice the Principle of Optimality:

Principle of Optimality: An optimal policy has the property that whatever
the initial state and initial decision are, the remaining decisions must
constitute an optimal policy with regard to the state resulting from the
first decision. (See Bellman, 1957, Chap. III.3.)

Or mathematically, to solve the problem from t to T optimally, we
can solve a sub-problem from t to s given t < s < T and extend s
to T.

It is relatively intuitive to reason the optimal substructure for most
problems, we will only focus how to build up the optimal solution in this
post.

Top-down vs. bottom-up

We are trained to solve the problem in the top-down approach: break
down a big problem to several smaller problems, recursively doing so
until the smaller problems can be comfortably tackled. CLRS points
out that the time complexity of top-down with memoization is the same
as the bottom-up approach, but the latter is preferred as the top-down
approach requires O(n)O(n)O(n) callstacks which caps the problem scope.

Look back vs. Look forward

We solve the DP problems by looking back the sub-problems; on the other hand,
some problems require us to look forward: the solution for the current
sub-problem depends on the sub-problems in the future. Technically, they
SHOULD be categorized as BFS, — the solution space is explored with the
breadth first search. They are discussed here just for the reference.

Given n balloons with designated values, burst balloon i will get
nums[left] * nums[i] * nums[right] coins, try to maximize the gain.

If we add two balloons with value of 1 on both sides, the
original problem become: with n + 2 balloons from 0 to n + 1 and
balloons[0] == balloons[n + 1] == 1, burst balloons from 1 to n,
maximize the profit.

If we burst balloons[i] first, the balloons[i - 1] and
balloons[i + 1] become adjacent. The solution to the sub-problem
grows complicated due to the bookkeeping of adjacent balloons. Instead,
we can divide-and-conquer the problem by focusing on the last balloon
to burst: if balloons[i] is the last balloon to burst, the accumulated
coins are:

Conclusion

In this post, we demonstrate several use cases of the dynamic programming from
different angles for a better understanding. The cornerstone of DP is how to
break down the problem to sub-problems for the optimal solution, then we can
build the solution in the bottom-up fashion.