1. What is dynamic planning?
I have read a lot of questions. Generally, I started to use DP to solve the problem. Then I wrote a nested for loop, which is not easy to understand, but I did solve it, what is dynamic planning? What are its characteristics? Let me copy a paragraph:
Dynamic Planning(Dynamic programming, DP), a method for solving complex problems by decomposing the original problem into a relatively simple subproblem. Generally, many subproblems are very similar. For this reason, the dynamic programming method tries to solve each subproblem only once, thus reducing the calculation workload. Once a subproblem has been solved, it is stored in memory, so that you can directly look up the table when you need to solve the same subproblem next time. This approach is particularly useful when the number of replay questions is about exponential growth in input size.
I have a new understanding of dynamic planning. The core of dynamic planning is to break down the problem into relatively simple sub-problems. Generally, there is a recursive idea, but it is generally solved using a for loop, which is more efficient. That is to say, recursion is also a dynamic planning, but the efficiency is not high.
2. Classic Question 1: Fibonacci series (Fibonacci polynomial)
Let's use an example:
What isFibonacci SeriesWhat about it?
Is a series like this...
We start from 0, that is, except that the number of 0th and the number of 1st are 1, The number following is equal to the sum of the first two.
2.1 use ordinary recursion to solve the Fibonacci series
[CPP]View plaincopy
- Int fib (int n ){
- If (n = 0 | n = 1 ){
- Return 1;
- }
- Return fib (n-1) + fib (n-2 );
- }
First, we do not consider negative numbers. The above method is very classical recursion. When N = 5: the calculation process of Fib (5) is as follows:
fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
The efficiency is very low, and the scale will increase exponentially. What is the problem with this solution? That is, some computed values are not saved, and they are always computed repeatedly.
2.2 Use Dynamic Planning for Solution
Fibonacci Series
We know that the problem above is that some calculated values are not saved. Modify the following and use two variables that have been calculated. Thanks @ for your suggestions:
[CPP]View plaincopy
- Int fib (int n)
- {
- Int Prev = 1, next = 1, TMP = 2;
- For (INT I = 2; I <= N; I ++ ){
- TMP = Prev + next;
- Prev = next;
- Next = TMP;
- }
- Return TMP;
- }
3. Classic Question 2 triangle (leetcode)
Given a triangle, find the minimum path sum from top to bottom. each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2], [3,4], [6,5,7], [4,1,8,3]]
The minimum path sum from top to bottom is 11
(I. e., 2 + 3 + 5 + 1 = 11 ).
This question is more difficult than the above Fibonacci number. When you get the next number, you can only find adjacent ones. For example, if the current step is 5, the next step can only be 1 or 8. That is to say, the next index may only be equal to the current index, or index + 1.
3.1 use recursion to solve the triangle
Our idea is to start from the header and notice that recursion mainly needs to process parameters and termination conditions. The termination condition here is to reach the last layer of the triangle. We need to constantly change the number of layers and index, because we need to compare two numbers. Program variable writing is coorse, mainly for understanding.
[CPP]View plaincopy
- Int minpath (vector <int> triangle, int index, int level ){
- If (Level = triangle. Size ()-1 ){
- Return triangle [level] [Index];
- } Else {
- Int current = triangle [level] [Index];
- Int nextfirst = minpath (triangle, index, level + 1 );
- Int nextsecond = minpath (triangle, index + 1, LEVEL + 1 );
- Int nextfirsttotal = nextfirst + current;
- Int nextsecondtotal = nextsecond + current;
- Return nextfirsttotal <nextsecondtotal? Nextfirsttotal: nextsecondtotal;
- }
- }
- Int minimumtotal (vector <int> & triangle ){
- If (triangle. Size () = 0) return 0;
- Return minpath (triangle, 0, 0 );
- }
3.2 use DP to solve the triangle
We noticed that recursion is very slow. To obtain the shortest distance of layer 1st, we need to first obtain layer 2nd. In order to find the 2nd layer, you must first find the 3rd layer. After a lot of pitfalls are laid, the results can be fully filled in. This topic is also very interesting. From top to bottom, we can change our thinking and find it from the bottom.
[ [2], [3,4], [6,5,7], [4,1,8,3]]
We can use an array to record the obtained shortest distance. At the beginning, we initialize the number of the last row, which is 2nd, and then calculate the shortest distance from the last row to the last rows. Because 1 + 6 <4 + 6, 1 + 5 <8 + 5, 3 + 7 <8 + 7. So we update this array to 7, 6, 10, 3. The last 3 is useless. So on:
The shortest distance from the last row to the last 3rd rows is updated to: 9, 10, 10, 3.
The shortest distance from the last row to the last 4th rows is updated to: 11,10, 10,3.
The first number 11 is the result we want.
[CPP]View plaincopy
- Int minimumtotal (vector <int> & triangle ){
- Int trianglesize = triangle. Size ();
- If (trianglesize = 0 ){
- Return 0;
- }
- // Initialize an array to record the shortest distance from the last row to the current row
- Vector <int> savemindistance (Triangle [trianglesize-1]. Size (), 0 );
- // The initial value is the value of the last row.
- For (INT I = 0; I <triangle [trianglesize-1]. Size (); ++ I ){
- Savemindistance [I] = triangle [trianglesize-1] [I];
- }
- Int first, second, current;
- First = Second = current = 0;
- // Search for 2nd rows from the last 1st rows
- For (INT I = trianglesize-2; I> = 0; I --){
- // When the nth row requires n + 1 Shortest Path and saves them
- For (Int J = 0; j <I + 1; j ++ ){
- Current = triangle [I] [J];
- First = Current + savemindistance [J];
- Second = Current + savemindistance [J + 1];
- // Save the shortest distance
- Savemindistance [J] = first <second? First: second;
- }
- }
- Return savemindistance [0];
- }
Slightly optimized, except for initializing the last line, it is also placed in the For Loop:
[CPP]View plaincopy
- Int minimumtotal (vector <int> & triangle ){
- Int trianglesize = triangle. Size ();
- If (trianglesize = 0 ){
- Return 0;
- }
- // Initialize an array to record the shortest distance from the last row to the current row
- Vector <int> savemindistance (Triangle [trianglesize-1]. Size () + 1, 0 );
- Int first, second, current;
- First = Second = current = 0;
- // Search for 1st rows from the last 1st rows
- For (INT I = trianglesize-1; I> = 0; I --){
- // When the nth row requires n + 1 Shortest Path and saves them
- For (Int J = 0; j <I + 1; j ++ ){
- Current = triangle [I] [J];
- First = Current + savemindistance [J];
- Second = Current + savemindistance [J + 1];
- // Save the shortest distance
- Savemindistance [J] = first <second? First: second;
- }
- }
- Return savemindistance [0];
- }
In general, dynamic planning is still difficult. First, the current question should be implemented using the idea of dynamic planning. Second, it is necessary to fully think about the entire question, what value to save, and how to for loop.
We use two questions to explore dynamic planning. Dynamic Planning is still difficult.