**Dynamic Programming**

For example, Fibonacci numbers are defined by the following recurrence:

F(0) = 0 F(1) = 1 F(i) = F(i-1) + F(i-2)It is easy to directly write a recursive algorithm according to this definition:

Fibonacci-R(i) 1 if i == 0 2 return 0 3 if i == 1 4 return 1 5 return Fibonacci-R(i-1) + Fibonacci-R(i-2)The complexity of this algorithm is O(2

It is easy to see that the algorithm can be rewrite in a "bottom-up" way, with intermediate results saved in a data structure. In this case, an array is enough. This gives us a "dynamic programming" version of the algorithm:

Fibonacci-DP(i) 1 A[0] = 0 // assume 0 is a valid index 2 A[1] = 1 3 for j = 2 to i 4 A[j] = A[j-1] + A[j-2] 5 return A[i]The complexity of this algorithm is O(n).

From this simple example, we see the basic elements of *dynamic programming*, that is, though the solution is still defined in a "top-down" manner (from solution to subsolution), it is built in a "bottom-up" manner (from subsolution to solution). Since the subsolutions to subproblems are kept in a data structure for possible later use, each of them is only calculated once.

*Memoization* is variation of dynamic programming that keeps the efficiency, while maintaining a top-down strategy.

Fibonacci-M(i) 1 A[0] = 0 // assume 0 is a valid index 2 A[1] = 1 3 for j = 2 to i 4 A[j] = -1 5 Fibonacci-M2(A, i) Fibonacci-M2(A, i) 1 if A[i] < 0 2 then A[i] = Fibonacci-M2(A, i-1) + Fibonacci-M2(A, i-2) 3 return A[i]The complexity of this algorithm is also O(n).

Dynamic programming is often used in optimization. For this kind of problem, the optimal solution corresponds to a special way to divide the problem into subproblems. When different ways of division are compared, it is quite common that some subproblems are involved in multiple times, therefore dynamic programming provides a more efficient algorithm than (direct) recursion.

The complexity of dynamic programming comes from the need of keeping intermediate results, as well as remembering the structure of the optimal solution, i.e., how the problem is divided into subproblems, step by step.

The product of a p-by-q matrix and a q-by-r matrix is a p-by-r matrix, which contains p*r elements, each calculated from q scalar multiplications and q − 1 additions. If we count the number of scalar multiplications and use it to represent running time, then for such an operation, it is p*q*r.

For matrix-chain *A _{1}A_{2}A_{3}*, if the three have sizes 10-by-2, 2-by-20, and 20-by-5, respectively, then the cost of

In general, for matrix-chain *A _{i}...A_{j}* where each

m[i, j] = 0 if i = j min{ m[i,k] + m[k+1,j] + PSince certain_{i-1}P_{k}P_{j}} if i < j

Example: Given the following matrix dimensions:

A_{1} is 30-by-35

A_{2} is 35-by-15

A_{3} is 15-by-5

A_{4} is 5-by-10

A_{5} is 10-by-20

A_{6} is 20-by-25

then the output of the program is

which means that *A _{1}A_{2}A_{3}A_{4}A_{5}A_{6}* should be calculated as

The matrix-chain multiplication problem can be solved by either a bottom-up dynamic programming algorithm or a top-down, memoized dynamic-programming algorithm. The running time of both algorithms is Θ(n^{3}), and the algorithm needs Θ(n^{2}) space. Please note that this algorithm determines the best order to carry out the matrix multiplications, without doing the multiplications themselves.

For sequence X and Y, their LCS can be recursively constructed. Let us define c[i, j] to be the length of an LCS of the sequences X_{i} and Y_{j}, then

Unlike substring, LCS does not need to be continuous.

Algorithm:

Time cost: Θ(m*n). Space cost: Θ(m*n).

(2) **Optimal Binary Search Tree**

If the keys in a (constant) BST have different probabilities to be searched, then a balanced tree may not give the lowest average cost (though it gives the lowest worst cost). Using dynamic programming, BST with optimal average search time can be built by comparing the expected cost of each candidate (i.e., with each node as root), in a way similar to the matrix-chain multiplication algorithm. The input of this algorithm are the probabilities for each key values to be searchered, as well as those of the other values not in the tree.