What is Dynamic Programming?
Let’s have a look at the definition on Introduction to Algorithm:
"Dynamic programming like the divide-and-conquer method, solves problems by combining the solutions to sub-problems.
Different from divide-and-conquer, dynamic programming applies when the sub-problems overlap — that is, when sub-problems share sub-sub-problems.
A dynamic programming algorithm solves each sub-sub-problems just once and then saves its answer in a table, thereby avoiding the work of recomputing the answer every time it solves each sub-sub-problems.
We typically apply dynamic programming to optimization problems. Such problems can have many possible solutions. Each solution has a value, and we wish to find a solution with the optimal (minimum or maximum) value."
Four steps:
1.Characterize the structure of an optimal solution.
2.Recursively define the value of an optimal solution.
3.Compute the value of an optimal solution, typically in a bottom-up fashion.
4.Construct an optimal solution from computed information.
Problems:
1 Rod cutting (IA – 15.1)
1.1 Analysis
Firstly we can frame the values rn for n >= 1 in terms of optimal revenues from shorter rods:
rn = max (pn, r1 + rn-1, r2 + rn-2, rn-1 + r1).
Here pn stands for the no-cut price and the rest combination means different way of cutting off.
We need to note that each cut will create two independent sub-problems so this is a optimal substructure. And we can get a simpler version of equation:
rn = max (pi + rn-1).
The trick to solve this problem in a high efficient is to only solve each sub-problem once and save its solution so that we can use them later rather than recompute them repeat. But it will use additional memory.
1.2 Two ways
1.2.1 Bottom-up with memorization
In this way, we just need to save the results of each sub-problems:
func(p, n)
let r[0...n] be a new array
for i = 0 to n
r[i] = -infinity
return func-aux(p, n, r)
func-aux(p, n, r)
if r[n] >= 0
return r[n]
if n == 0
q = 0
else q = -infinity
for i = 1 to n
q = max(q, p[i] + func-aux(p, n-i, r))
r[n] = q
return q
1.2.2 Bottom-up method
In this way, we sort the sub-problems by size and solve them in size order from smallest. So when we first see a sub-problem, we have already solved all of its prerequisite sub-problems:
func(p, n)
let r[0...n] be a new array
r[0] = 0
for j = 1 to n
q = -infinity
for i = 1 to j
q = max (q, p[i] + r[j-i])
r[j] = q
return r[n]