[Dynamic Programming] The strongest and most detailed ideas and templates (C++)

This article is written according to the framework of Rikou Dynamic Programming Intensive Lectures (1), (2) and (3).

Intensive Lecture on Dynamic Programming (1) - LeetBook - LeetCode's favorite technology growth platform for global geeks

Table of contents

The characteristics of a dynamic programming problem

1.1 Overlapping sub-problems: Sub-problems appear repeatedly (the recursive tree can be clearly seen)

1.2 Optimal substructure

1.3 The difference between greedy and dynamic programming

1.4 No Consequences: How to Properly Define the Problem

Optimization problem - There are two difficulties in dynamic programming:

1.5 Template frame (top-down, bottom-up)

general recursive method

Optimization: top-down with memo

Optimization: bottom-up

1.6 How to write the state transition equation


The characteristics of a dynamic programming problem

  1. The idea of ​​dynamic programming is used to solve the optimization problem.
  2. The original problem can be described by a recursive formulation including sub-problems.
  3. Optimal substructure: The optimal solution of the original problem can and must be obtained from the optimal solution of the subproblems.
  4. Overlapping sub-problems: Some sub-problems appear repeatedly during the solution process, resulting in a large number of repeated calculations, so use ① memorized search (top-down method with memo) (optimized version of ordinary recursion) ② bottom-up Methods

1.1 Overlapping subproblems

Overlapping sub-problems: Some sub-problems appear repeatedly during the solution process, resulting in a large number of repeated calculations, so use ① memorized search (top-down method with memo) (optimized version of ordinary recursion) ② bottom-up Methods 

Example: Recursive tree for cutting steel bars (see 2 for details)

1.2 Optimal substructure

Review what type of problem does dynamic programming solve? ——Optimization problem (optimization problem), then the most substructure said: the optimal solution of the original problem is composed of the optimal solutions of related sub-problems.

And these subproblems can be solved independently. And the optimal solution of the original problem must be "recursively transferred" from the optimal solution of the sub-problem after the sub-problem is found. Ask out.

1.3 The difference between greedy and dynamic programming

1. About the optimal substructure

Greedy: the optimal solution of each step must include the optimal solution of the previous step, and the optimal solution before the previous step does not need to be recorded
Dynamic programming: the global optimal solution must contain a local optimal solution, but not necessarily the previous step Local optimal solution, so it is necessary to record all previous local optimal solutions
2. Combination of sub-problem optimal solutions into original problem

Greedy: If all sub-problems are regarded as a tree, greedy starts from the root and traverses the optimal subtree each time. The optimal here is the optimal in the sense of greed. At this time, there is no need to know all the subtrees of a node, so a complete tree cannot be formed
Dynamic programming: dynamic programming needs to find the optimal solution for each subtree until the value of each leaf below, and finally get a tree Complete tree, after all subtrees get the optimal solution, combine them into answer
3, result correctness

Greedy cannot guarantee that the final solution obtained is the best, and the complexity is low.
The essence of dynamic programming is an exhaustive method , which can guarantee that the result is the best, and the complexity is high.

Author: FennelDumplings
Link: https://leetcode.cn/leetbook/read/dynamic-programming-1-plus/xcrktd/
Source: LeetCode
Copyright belongs to the author. For commercial reprint, please contact the author for authorization, for non-commercial reprint, please indicate the source.

1.4 No Consequences: How to Properly Define the Problem

Optimization problem - There are two difficulties in dynamic programming:

  • How to define the original problem and sub-problem f(n), because sometimes the problem given by the title may be vague, so we have to go through some conversions when solving.
  • How to deduce the original problem f(n) through the sub-problems f(1), f(2), … f(n - 1), that is, how to write the state transition equation

Li Yudong's "Advanced Guide to Algorithm Competition", the excerpts are as follows:

In order to ensure that the calculation of sub-problems can be carried out sequentially and without repetition, dynamic programming requires that the sub-problems that have been solved are not affected by subsequent stages. This condition is also called "no aftereffect". In other words, the traversal of the state space by dynamic programming constitutes a directed acyclic graph, and the traversal is a topological order of the directed acyclic graph . The nodes in the directed acyclic graph correspond to the "states" in the problem, and the edges in the graph correspond to the "transitions" between states. The selection of transitions is the "decision" in dynamic programming .

My explanation:

"Directed acyclic graph" and "topological order" indicate that each sub-problem is only solved once, and the process of solving the problem in the future will not modify the results of the previously solved sub-problems; in other words: if the sub-problems solved in the previous
stage The result contains some uncertain information, which makes the sub-problems solved in the later stage unobtainable or difficult to obtain. This is called "consequence effect". The sub-problem we split for the first time in the current problem is " Consequences" (you can turn to the above to see again);
the solution to "consequences" is to fix the places that need to be classified and discussed, and record more results. At the code level, it is manifested as:
increasing the dimension of the state array, for example: the stock series problem of "Likou"; defining the
state more carefully and accurately, for example: Question 124 pushed the day before yesterday: the state definition only solves the path from the left and right subtrees one of the subtrees of .

Author: liweiwei1419
Link: https://leetcode.cn/problems/maximum-subarray/solution/dong-tai-gui-hua-fen-zhi-fa-python-dai-ma-java-dai/
Source: Leetcode ( LeetCode)
copyright belongs to the author. For commercial reprint, please contact the author for authorization, for non-commercial reprint, please indicate the source.

2. The large frame of the template (top-down, bottom-up)

2.1 General recursive methods

Example: "Introduction to Algorithms" P205 Cutting Steel Bars

Give you a steel bar, you can cut it into several small parts (or you don't need to cut it), find a way to make this steel bar sell for the most price.

A comparison table of how much steel bars of various lengths can buy

General recursive code:

int re(int n)
{
	if(n==0)  //钢条长度为零的时候,返回零
		return 0;
	int q=N[n];
	int i;
	for(i=1;i<n;i++)  //遍历k~L-k每一种情况,找到里面的最大值,k为零时为不切割的情况,q=N[n]已经储存,不需要考虑
		q=maxx(q,r[i]+re(n-i));
	r[n]=q; //钢条长度为n时最多可以买到价格q
	return q;
}

2.2 Optimization: top-down with memo

1. Template

(1) Check the memo, if (stored) {directly return the result};

else{

        (2) Obtain the result recursively according to the general method.

        (3) Save the results obtained by the general method

}

 2. Ideas for solving problems by cutting steel bars

Imagine that the steel bar is composed of two parts that have been cut and those that have not been cut (the left side has been cut, and the right side has not been cut). For the uncut part, its optimal price can be calculated recursively.

(1) The number of recursive layers and the scale of each layer

Number of recursive layers: [1,n], the steel bar has a total of n segments, which can be divided into left and right at any position, and recursive to the right.

The scale of each layer: For each layer of recursion, if the length left on the right side of the previous layer is len, then the cuttable size of this layer is [1, len], and each layer of recursion must traverse this len selection.

(2) State transition equation

For the steel bar whose length (scale) is i to be cut, its value is recorded as dp[len] (i takes [1, len]), and the state transition is to try to cut each section of the layer, and take one of them maximum benefit. If it is cut, the cut part is recorded as the left side, and the value on the left side is price[i], and it is recursive on the right side to obtain the maximum value of the income on the right side, which is recursion(price,searched,ni). So the equation is:

for(int i=1;i<n;++i){ 

        q=max(q,price[i]+recursion(price,searched,n-i));

}

(3) Code

recursion(vector<int> price,vector<int> searched, int n){
    if(searched[n]!=-1){     //如果之前已经记录了,就直接查表并返回
        return searched[n];
    }

    int q=INT_MIN;           //如果备忘没有记录,就按普通方法递归
    for(int i=1;i<n;++i){    //原问题依赖的子问题规模,不同问题不同
            q=max(q,price[i]+recursion(price,searched,n-i));//状态方程,不同问题不同
    }
    searched[n]=q;           //普通方法,保存结果
return q;
}

(4) Parameter explanation

n: input size, searched: memo of one-dimensional array, i: the range of i means that the size of the sub-problem dependent on the size of n is 1~n, and the operation in i means the sub-problem of the size of 1~n in turn. The answer takes the maximum value. That is, the optimal substructure . (Review: Optimal substructure: the optimal solution of the original problem is composed of the optimal solutions of related subproblems. ) (PS: We wrote this program from top to bottom, so we assume that the answer is known .In fact, the answer is returned to the superior function during the recursive "return") q: The optimal solution to the original problem with a scale of n.

2.3 Optimization: bottom-up

1. Template

int n; // input size

(1) Set the array for storing the state. The state transition equation depends on how many dimensional variables to transfer, and the number of dimensional arrays is set, vector<int> dp(n+1);

(2) Boundary case dp[0]=...

(3) General situation (from small to large scale):

for(int i=0;i<n;++i){

        dp[i]=……

}

Detailed explanation of the bottom-up approach:

(1) Scale from small to large

The scale from small to large and its state transition equation often need to be imagined with your brain, because the scale given in the title is n is not 1~~~ Let’s imagine this: what happens if the scale (here is the length of the steel bar) is 0 ? What about a scale of 1? What about a scale of 2? How is the result of scale 2 transferred from the result of scale 1?

For example, the steel bar with a scale of 2 in this question can be cut with 1 on the left, and the steel bar with a scale of 1 is left on the right (in the bottom-down thinking, the right can be understood as the steel bar that has been processed), that is to say, the right The steel bar of scale 1 that has been processed, plus the length of 1 is the steel bar of scale 2, the value of the steel bar of scale 1, plus the value of the left steel bar, is the value of the steel bar of scale 2 la~. Similarly, the value of the steel bar with a scale of 3 can be obtained from the value of the steel bar with a scale of 1 on the right + the value of a steel bar with a scale of 2 on the left, or the value of the steel bar with a scale of 2 on the right + a steel bar with a scale of 1 on the left The value of the steel rods can be obtained, so which one should you choose? Of course, choose the one with the largest income~.

(2) State transition equation

Bottom-down state communication, c++ common array storage, I choose to use STL dynamic array vector

Let the value of the steel bar with size i be dp[i], the state transition equation:

dp[0]=0;//Boundary conditions, the length of the steel bar is 0, and the income is 0

 dp[i]=max(dp[i],dp[ij]+price[i]);//general situation

    int CutBar(vector<int> price, int n) {
        vector<int> dp(n+1);
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {//钢条规模
            for (int j = 1; j <=i; ++j) {//在该规模下,依次记录切下长度为j的钢条的收益,取其中的最大值,最少切1,所以j=1
                dp[i] = max(dp[i], dp[i - j] + price[j]);
            }
        }
        return dp[n];
    }

3. How to write the state transition equation (see another link for details~)

[C++] Dynamic Programming State Transition Equation (Single String) - Bluepingu's Blog - CSDN Blog

Guess you like

Origin blog.csdn.net/icecreamTong/article/details/127618072