Dynamic programming can be applied to optimization problems that exhibit two properties:
There are two implementation strategies for dynamic programming: top-down (memoization) and bottom-up (tabulation).
Memoization. Implements dynamic programming as a recursive procedure. To solve a sub-problem, we simply call the recursive procedure on the subproblem, with one crucial optimization: whenever we finish a recursive call, we cache its result, and whenever we begin a recursive call, we return the cached result if there is one.
Bottom-up dynamic programming. Implements dynamic programming by identifying all the subproblems of a given problem and the dependencies among subproblems. The subproblems are then solved in dependency order (i.e., starting a problem that has no subproblems, and working up to the original problem). Bottom-up dynamic programming is typically implemented using loops rather than recursion.
Backtracing. It is often convenient to solve optimization
problems in two steps. The first step computes the optimal value of
the solution using dynamic programming. The second
step, backtracing, computes the optimal solution by using a
top-down procedure. Backtracing saves the cost of computing and
storing the optimal solution to every subproblem.
Top-down computation of the optimal solution to the problem
typically involves selecting among a set of alternative subproblems
for a problem. Since we have already cached the optimal values to
each subproblem, we may use the optimal value to select among the
alternatives rather than searching through all of them.
public int f(int n, int m) { if (n == 0) { return m; } return f(n - 1, m + 1) + f(n - 1, m); }What is the time complexity (in Θ notation) of f, as a function of n and m? What is its time complexity if the function is memoized?