Keywords: Dynamic Programming; Recursive Methods
[Why write this article] talking about algorithms in zero order
[Here are the symbols and formulas used in this series of articles.] Here we are talking about algorithm (outside of this article) symbol tagging and basic mathematical formulas.
Dynamic Planning, Dynamic Programming. The programming here is not translated into programming because the programming here refers to a tabular method. In fact, this also implies the nature of DP, using a table to store the intermediate results of the subproblem. (Examples will be provided later)
It is similar to the divide and conquer algorithm, but the difference is that the divide and conquer algorithm divides the original problem into several independent subproblems to solve them one by one, dynamic Planning is a solution for overlapping sub-problems.
Currently, design DP has two main ideas:
One is to use the recursive method, that is, to solve the problem first using a recursive method, and then use a table to save the intermediate results in the recursive. Does this avoid the inefficiency of repeated calculation in recursion? If you need to calculate something you have previously computed, you can directly look up the table. In a word, you can write recursive first, and then you can write the DP Method better than Hulu. The difficulty here is how to find the recursive. This idea is also provided in the introduction to algorithms. The first three examples are all from introduction to algorithms.
Another idea is exhaust search, which seems to be a method invented by our teacher. Here is a Kirk paper,How to design dynamic programming algorithms sans recursionIf you are interested, you can take a closer look. I will also give a brief example of this method.
In the following example, most of the Code is pseudo code, which is designed to define strate idea. Saves time. The backtrack process is saved in the code, that is, the value of optimal solution is obtained, and the process of construct optimal solution is saved. This usually uses an array to record it.
First, let's look at a simple example (in fact, it's not hard for O (∩) O ~)
Example: Rod-cutting problem(Wood cutting problem. How does it feel changed after translation? Success ...)
Input: There is a n-meter-long wood, and a price table, the table is as follows:
Length I 1 2 3 4 5 6...
Price Pi 1 5 8 9 10 17...
Obviously, wood with a length of 1 meter can be bought for 1 yuan, and wood with a length of 5 meters can be sold for 10 yuan, and so on.
Output: Find a cut method to make the most money.
Obviously, the main idea of recursion is that after I cut a knife, I divided it into two sections. I sold it at the table price, and I considered it a new subproblem, continue as a new parameter of my function. Isn't it recursive? (* ^__ ^ *) But the problem is how to cut the knife. That's right. Let's find the maximum value, that is, max _ {I = 1 to n} Pi + Cut (n-I ).
Therefore, recursive functions should be:
Cut (P, n) {// P is my table, and n is the length of wood
If n = 0
Return 0;
Q =-infinity
For I = 1 to n
Q = max (q, P [I] + Cut (P, n-I ))
Return q;
}
Then, write DP Based on the recursive
Cut (P, n ){
For (int I = 1; I <= n; I ++ ){
Q =-infinity;
For (int j = 1; j <= I; j ++)
Q = max (q, P [j] + r [I-j]);
R [I] = q;
}
Return r [n];
}
Example: Longest common Longest subsequence problem
Problem description: this, very... Obviously, I don't know ,... Look here http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
Of course, here we also need to find recursion first. Suppose I have two sequence, one is X, the length is n, the other is Y, and the length is m.
Now suppose I have two points. One is I, which refers to the last element of X and the other is j, which refers to the last element of Y. Our recursion is divided into three situations.
1) if X [I] = Y [j] Then LCS (X [I], Y [j]) = LCS (X [I-1], Y [J-1]) + 1
This is obvious, because a group of public elements is found, it depends on the number of public elements remaining.
2) If X [I]! = Y [j] So LCS (X [I], Y [j]) = max (LCS (X [I-1], Y [j]), LCS (X [I], Y [J-1])
This is also very easy to be the same, that is, if we find different, we will remove the last one of X or Y, and then compare it with another complete one. In this way, we will remove X or the last one of Y, there are two possibilities, so we need to find one of max in the middle.
3) if I = 0 or j = 0, return 0
Because a sequence is complete.
So recursion is obvious here:
LCS (n, m ){
If m = 0 | n = 0
Return 0;
If (X [n] = Y [m])
Return LCS (N-1 m-1) + 1;
Else
Return max (LCS (m-1), LCS (n-1, m ));
}
Now with recursion, we should be able to write it into the tabular DP.
Initialization: the first column in the first row of Table LCS is set to 0.
For I = 1 to m
For j = 1 to n
If (X [I] = Y [j])
LCS [I, j] = LCS [I-1, J-1] + 1;
Else
LCS [I, j] = max (LCS [I, J-1], LCS [I-1, j]);
For example, the problem of multiplication of matrix chains to the minimum number of times
Specific Problem description, see here http://en.wikipedia.org/wiki/Matrix_chain_multiplication
First, let's formalize our input, matrix Chain {A1, A2,..., An}. For matrix Ai, It is a matrix of Pi-1 * Pi. M [I, j] indicates the minimum multiplication times of {Ai, Ai + 1,..., Aj} multiplied (strongly calls for a plug-in similar to Latex)
The idea of recursion is actually a bit similar to that of the first question. Let's first look at how to recursion.
First, we need to find a point to separate the chain, this is equivalent to converting this problem to the problem of least multiplication of the matrix chain in the first part of the point and the problem of least multiplication of the matrix chain in the last part of the point.
But how can I find this point? Like in the first example, we can directly traverse and find.
So our recursion should be like this:
M [I, j] = min _ {I <= k <= j} (m [I, k] + m [k + 1, j] + Pi-1 * Pk * Pj)
Of course, this is for I! = J. What happens when I = j? It is obviously 0, because our matrix chain is just a matrix.
That is
Matrix-chain (I, j ){
If (I = j)
Return 0;
Q = infinity;
For k = I to j
Q = min (q, Matrix-chain (I, k) + Matrix-chain (k + 1, j) + Pi-1 * Pk * Pj );
Return q;
}
Now, we can turn this recursive item into DP. There is specific code in the introduction to algorithms. Here we only write a brief introduction.
Let's assume that we save the intermediate result as an n * n table m.
Initialization: m is initialized to 0.
For l = 2 to n
For I = 1 to n-l + 1
J = I + L-1
M [I, j] = infinity
For k = I to J-1
Q = m [I, k] + m [k + 1, j] + Pi-1 * Pk * Pj;
If (q <m [I, j])
M [I, j] = q;
Return m
Refer to the recursive example above. This DP code is not hard to understand.
Longest return sequence
The back-to-source sequence is the same for reading from left to right and from right to left. For example, civic, racecar, and aibohphobia. All strings with a length of 1 are input.
The objective is to give a string to find the longest return sequence of the string, such as character and carac.
The following describes the method invented by our teacher,Exhausted search addition and subtraction~ OrExhaust search with pruningNice to listen to O (∩ _ ∩) O ~
The main idea of this method is to first construct a tree to make the leaf node of the tree a possible final solution, in other words, the final answer must exist in a leaf node at the bottom of the tree. Obviously, in general, this tree is still relatively well constructed. After all, there is no need to consider any conditions, that is, an exhaust expansion process. But as the tree expands, the branches become more and more, this certainly does not make sense, so we need to find some pruning rules to trim this tree. In fact, after half a day, it must be vague. Let's take a look at the example first. I think it is quite a bit of a sense to read the example later.
Longest incremental string for Example
Problem description: http://en.wikipedia.org/wiki/Longest_increasing_subsequence_problem (actually obvious)
Assume that one of my input strings is X1, X2, X3,..., Xn, And we construct an enumeration tree ).
How to construct this tree through the above figure, I think it should be obvious, and my solution must be on a leaf node.
Here, the most important thing is to find the prunning rules.
Of course, for this LIS question, what is our prunning rules?
1) Obviously, if the sequence of a node is not incremental, we should cut it off directly because... Apparently ~
2) For nodes at the same layer, if two nodes at the same layer have the same incremental length, we should keep that sequence, and the value of its last element is the smallest. (How do you feel so awkward. For example, if a node on a layer is 2 4 7, and another node is 3 4 6, their incremental length is 3, but because of 6 <7, therefore, we should keep the node and cut the node.
3) For nodes at the same layer, if the values of the last element of the two nodes are the same, we should keep the node with a long length and cut the node with a short length. For example, if a node is 2 3 6 and a node is 3 6, we keep 2 3 6 and cut off 3 6.
In fact, we can find that (2) (3) is actually one thing, so we can write our DP according to (1) (2), or according to (1) (3) write our DP.
Shenma? In this way, you can? Yes, it's too simple, sometimes naive ???!!!
According to (1) (2)
A [l, s] This is A cell in table A. l indicates the level of the tree, s indicates the length, A [l, s] indicates the value of the last element on the node.
For l = 1 to n
For s = 1 to l
If A [l, s] then
A [l + 1, s] = min (A [l + 1, s], A [l, s])
If A [l, s] <X _ {l + 1} then
A [l + 1, s + 1] = min (A [l + 1, s + 1], X_l + 1)
According to (1) (3)
A [l, s] is A cell in table A. l indicates the level of the tree. s indicates the subscript of the last element of the node, that is, the last element is Xs, A [l, s] indicates the length.
For l = 1 to n
For s = 1 to l
If A [l, s] then
A [l + 1, s] = max (A [l + 1, s], A [l, s])
If Xs <X _ {l + 1} // if a new element is added on the l + 1 layer, the length of our sequence can be increased.
// A [l + 1, s] + 1 indicates adding this new element to the original element.
// A [l + 1, l + 1] indicates finding the longest sequence ending with the new element X _ {l + 1.
A [l + 1, l + 1] = max (A [l + 1, l + 1], A [l + 1, s] + 1)
For example, find the change
Input: several Coins, X1, X2,..., Xn, and a numerical value L
Output: Can I use the Coins of several denominations to make it exactly the amount of L.
Similarly, in this case, it is the same as the method used to construct the tree in the LIS.
Now let's take a look at our prunning rules.
1) if the sum of the nominal values of a node is greater than L, cut it off directly.
2) at the same level, if there are two nodes with the same denomination, just cut off one.
Well, this pruning rule is simpler.
Write DP below.
Sum (l, s) = 1 is used to represent the Level l. The value s can be obtained.
For l = 1 to n
For s = 1 to L
If sum (l, s) then
Sum (l + 1, s) = 1
If sum (l, s) + X _ {l + 1} <L
Sum (l + 1, s + X _ {l + 1}) = 1
Backpack Problems
Input: several items, X1, X2 ,..., xn, their value is V1, V2 ,..., vn, their weight is W1, W2 ,..., wn, and give you a weight limit L,
Output: in the case of weight sum less than L, which items are used to maximize their values.
Similarly, in this case, it is the same as the method used to construct the tree in the LIS.
Now let's take a look at our prunning rules.
1) if the total weight of a node exceeds L, cut it off.
2) at the same layer, if two nodes have the same weight, the values are smaller.
3) In the same layer, if two nodes have the same value, cut off the weight big ones.
Like the LIS example, we can use (1) (2) or (1) (3)
Write DP below.
According to (1) (2)
Sum (l, s) indicates the value of a node whose weight on the l layer is s.
For l = 1 to n
For s = 1 to \ sum _ {I = 1} ^ l Wi)
If sum (l, s) then
Sum (l + 1, s) = max (sum (l + 1, s), sum (l, s ))
If sum (l, s) + X _ {l + 1} <L then
Sum (l + 1, s + X {l + 1}) = max (sum (l + 1, s + X {l + 1}), sum (l, s) + V _ {l + 1 })
According to (1) (3), I will not write it here. Like the above principle, it should be easier to write it out than the gourd painting.
Example: String pattern Matching
Input: A series of strings X [m], each character with cost: A (3) B (4) B (6) A (2) C (2) C (5) A (1 )...., suppose the length of the string is m; A pattern Y [n]: a B A C ..., assume that the length of pattern is n.
Output: Find A cost and the smallest string, and match pattern (here, matching each character can have an interval in the original string, for example, the string is A (3) B (4) B (6) A (2) C (2) C (5) A (1), pattern is ABAC, I can take A (3) B (6) A (2) C (5), so the total cost is 3 + 6 + 2 + 5 = 16)
As the last example, we will use the two methods mentioned above to solve this problem.
First, let's look for Recursive methods.
In fact, this idea is similar to finding the longest Public String. Scan the given string sequence or pattern from the last character to scan forward.
Cost (I, j) has the following situations,
1) I = 0. infinity is returned directly. Because this means that we did not find our pattern Y in the given string X.
2) If j = 0, 0 is returned directly. Indicates that we have found the target.
3) if X [I]! = Y [j], return Cost (I-1, j) at this time, because our pattern does not match, we can only explore the next character of X. Note that j has not been reduced by one here. Obviously, because pattern does not match (a few more)
4) if X [I] = Y [j], return min (Cost (I-1, J-1) + c (X [I]), Cost (I-1, j )) here, min indicates that when we encounter a match, we have two options: Use the character in X as one of our final matches, or we can discard this match, continue searching.
The recursive is completely written below.
Cost (m, n ){
If (m = 0)
Return infinity;
If (n = 0)
Return 0;
If (X [m]! = Y [n])
Return Cost (m-1, n );
Else
Return min (Cost m-1, n-1) + c (X [I]), Cost (m-1, n ));
}
The old rules below are as follows: Write DP Based on recursive call.
Assume that our table is an m * n table.
Obviously, during initialization of this issue, we need to initialize the Row m = 0 To infinity and initialize the column n = 0 to 0. after initialization,
For (int I = 0; I <m; I ++)
For (int j = 0; j <n; j ++ ){
If X [I]! = Y [j]
Cost [I, j] = Cost [I-1, j];
Else
Cost [I, j] = min (Cost [I-1, J-1] + c (X [I]), Cost [I-1, j]);
}
Now let's consider how to use the exhaust search method to solve this problem.
It is also the same tree as the figure above. We know that the size of X is m, so we can enumerate his string, a total of 2 ^ m, and the pattern we finally matched must be one of the 2 ^ m, therefore, we construct our tree based on this idea. This tree has a total of m layers. The node A [l, s] in each layer is represented in the l layer and matches the minimum cost of s characters in pattern.
Obviously, our prunning rules is for two nodes in the same layer. If they match the same length, that is, s equals, we should prune the cost.
For (int l = 0; l <m; l ++)
For (int s = 0; s <l; s ++ ){
If A [l, s] then
A [l + 1, s] = min (A [l + 1, s], A [l, s]);
If X [l + 1] = Y [s + 1]
A [l + 1, s + 1] = min (A [l + 1, s + 1], A [l + 1, s] + cost (X [l + 1])
}
Finally, the exhaust search method is not easy to understand at all, so we recommend that you take a look at the teacher's tutorial paper.How to design dynamic programming algorithms sans recursion.
========================================================== ========================================================== ==========
This work is licensed using the "knowledge sharing" signature 2.5 mainland China license agreement. This blog uses the "knowledge sharing" signature 2.5 mainland China License Agreement for permission. This blog is copyrighted by the author. You are welcome to reprint it without the author's consent.Do not randomly delete any content in the articleAnd on the Article PageThe original text connection is given clearlyOtherwise, you are entitled to pursue legal liability. If you have any questions or authorization negotiation, please leave a message for me.
Http://www.cnblogs.com/Gavin_Liu)