In the C++ algorithm advanced series, let’s talk about the two brushes of dynamic programming

1 Introduction

递归and 动态规划are the two leaders in the algorithm world. If you want to enter the door of algorithms, you must understand and master the essence of these two algorithms. Once you understand the essence of 2this algorithm, coupled with an in-depth understanding of complex data structures such as trees and graphs, you can solve most of the algorithm problems.

This article uses several typical cases to talk about the dynamic programming algorithm again. In fact, the dynamic programming algorithm is also 2a brush.

  • Find all alternatives for the current subproblem, choosing the largest or smallest value among all alternatives.
  • The optimal solution to this subproblem is an option for the next subproblem. Finally deduce the final result. Each sub-problem only needs to care about the results of its dependent sub-problems without paying attention to its implementation process.

The basic concept of dynamic programming: step by step, choose the best every time, and finally achieve the best.

Here are a few cases to understand how dynamic programming works step by step.

2. MostA

2.1 Problem Description

Now suppose there is a special keyboard containing the following keys:

  • Key 1: (A): Print one on the screen A.

  • Key 2: (ctrl-A): Select the entire screen.

  • Key 3: (ctrl-c):copy selected area to buffer

  • Key 4: (ctrl-v) :Output the contents of the buffer to the end of the last input and display it on the screen.

Now, you can only press the key Ntimes (using the above four keys), how many can be displayed on the screen at most A?

2.2 Sample

2.2.1 Sample1

Input: N =3
Output: 3
Explanation: We can display up to three on the screen Aby pressing the keys in the following order:
A,A,A

2.2.2 Sample2

Input: N = 7
Output: 9
Explanation: We can display at most on the screen 9, Apress the keys in the following order
A,A,A,ctrl-A,ctrl-c,ctrl-V, ctrl V

2.3 Problem analysis

This question is a question of seeking the maximum value, which can be realized using dynamic programming. As mentioned earlier, to use the dynamic programming algorithm, we must first know which options are available for each sub-problem.

Tips: As far as this question is concerned, the number of different keystrokes can be considered as a sub-problem.

To output on the screen , that is, to change the number of characters Aon the screen , there are options:A2

  • Press the key directly A. Only one keystroke is needed to output 'A'.
  • Copy on screen A. Press first ctrl+A、ctrl+Cto add content to the buffer, and then press repeatedly to ctrl+voutput letters on the screen A.

Which one is the best choice under different number of keystrokes?

What the dynamic programming algorithm does in this question is:

  • From the accumulation in the small-scale state to the result in the large-scale state. What this question needs to calculate is the number of children and mothers when the number of keys changes A.
  • When the number of state quantities changes, it is necessary to choose the most ideal solution.

Problem-solving process:

You can first define a one-dimensional dparray. It is used to store the number of sub-parents in different states A.

1.png

Now analyze which option can get the most ideal result under different times.

  • When the number of key presses 1is . In this state, it is only possible Ato output parent and child by pressing the key A.

2.png

  • When the number of key presses 2is . It is also only possible to output the letter A by directly pressing the A key. At this time, the number of letters on the screen is dp[2]=dp[1]+1.

3.png

  • When the number of key presses 3is . By directly pressing the 'A' key, A can also be output by copying.

    The number of directly pressed Akeys is dp[3]=dp[2]+1.

4.png

复制输出`A`。因复制的前置条件是要先按下`ctrl+A、ctrl+C`,意味着需要消耗掉2次按键,`ctrl+V`复制的内容应该是`dp[0]`位置字母`A`的个数。

5.png

​ Obviously, there are more letters output by direct keys A. It is the best scheme to choose to directly press the sub-keys in the two schemes.

4.png

  • When the number of key presses 4is .

    APress the key directly to input, and the characters Aon the screen are .A4

6.png

When outputting on screen using a copy scheme . There are two ways to copy 2:

At dparray 1position ctrl+A, 2at ctrl+C. What is copied in this way is dp[0]the number of sub-parents of the position.

7.png

at dparray position 2, ctrl+Aat 3position ctrl+C. What is copied in this way is dp[1]the number of sub-parents of the position.

8.png

Encoding implementation:

#include <iostream>
using namespace std;

//一维动态数组
int dp[100]= {0};
//按键次数
int n;
int main(int argc, char** argv) {
   	cin>>n;
	for(int i=1; i<=n; i++) {
		//直接按下A键
		dp[i]=dp[i-1]+1;
		//如果复制
		for( int j=2; j<i; j++ ) {
			dp[i]=max( dp[i],dp[j-2]*(i-j+1) );
		}
	}
    cout<<dp[n];
	return 0;
}

3. Longest increasing subsequence

3.1 Problem description

Given an unordered array of integers, find the length of the longest ascending subsequence in it.

Example:

  • enter:[10,9,2,5,3,7,101,18]

  • output:4

Explanation: The longest ascending subsequence is [2,3,7,101], and its length is 4.
Explanation: There may be multiple combinations of the longest ascending subsequence, you only need to output the corresponding length. The difference between a subsequence and a substring is that the substring is continuous, but the subsequence is not necessarily continuous.

### 3.2 Problem Analysis

How to solve this problem using dynamic programming idea.

  • Create a one-dimensional dynamic dparray. Record the length of the subsequence when the size of the data in the array changes. The initial value 1is that the sequence is its own subsequence.

9.png

  • Scan the original array from left to right, and when the data is scanned 10, obviously, the number of its subsequences is 1.

10.png

  • When the data is scanned 9, it 10is compared with the previous one. Because it is smaller than it, 9it cannot contribute to the incremental subsequence, and the number of the original subsequence is retained.

11.png

  • When scanned 2, dpthe value in the corresponding array is 1.

12.png

  • When scanned 5, its ratio is 10、9small, but the ratio 2is large, which can 2become an increasing subsequence of the current state value.

13.png

  • When the scan arrives , 3the longest subsequence should be added to the longest subsequence at the end because the ratio is large .3221

14.png

  • When the scan arrives 7, because 7the ratios 2,5,3are all large, it is necessary 2、5、3to find the maximum value in the longest subsequence at the end. The characteristic of dynamic programming is that when the state changes, it is often necessary to choose the best among multiple options.

15.png

  • In the same way, when it is scanned 101, because it is larger than all the previous numbers, you need to dpfind the maximum value in the already filled array and add it 1.

16.png

  • By the same principle, dpthe values ​​in the final array should look like this.

17.png

3.3 Encoding implementation

#include <iostream>
#include <cstring>
using namespace std;

//原始数组
int nums[]= {10,9,2,5,3,7,101,18};
//一维动态数组
int dp[100]= {1};

int main(int argc, char** argv) {
	int size=sizeof(nums)/sizeof(int);
	//最长子序列
	int maxVal=1;
	//遍历原始数组
	for( int i=0; i<size; i++ ) {
		dp[i]=1;
		//以 i 为当前位置,向原始数组之间扫描
		for( int j=i-1; j>=0; j-- ) {
			if( nums[i]>nums[j] ) {
				dp[i]=max(dp[i] ,dp[j]+1 ) ;
			}
		}
		if(maxVal<dp[i]) maxVal=dp[i];
	}
	for(int  i=0; i<size; i++)
		cout<<dp[i]<<"\t";
	cout<<"\n最长子序列长度:"<< maxVal<<endl;
	return 0;
}

4. Minimum path sum

4.1 Problem description

There is a two-dimensional array numsin which the elements are all non-negative integers . Now you are standing in the upper left corner, you can only move to the right or down , and you need to reach the lower right corner. Now please calculate, what is the minimum sum of the path you have passed?

A two-dimensional array is shown in the figure below.

18.png

This question is a typical 动态规划类question type.

The basic process is as follows:

  • Based on the basic idea of ​​dynamic programming, first create a two-dimensional dparray. Store the sum of the shortest paths from the starting location to each location in the table. The basic routine of dynamic programming is step by step. If the path sum from the starting point to each location can be guaranteed to be the smallest, the shortest path sum to the destination can naturally be obtained.

19.png

  • dpPopulate the table with some obvious values ​​first, also known as base case. For example, the shortest path of the starting point is itself, as shown in the following figure:

20.png

  • dpThe value of the first row in the table is only affected by the value on the left, there are no multiple choices, and it is easy to find out. Its value is dp[0][i]=dp[0][i-1]+nums[0][i].

21.png

  • dpThe value of the first column in the table is only affected by the upper edge value, and there is no multiple selection, its value is dp[i][0]=dp[i-1][0]+nums[i][0].

22.png

  • For values ​​at other positions, you need to select the minimum value from the values ​​on the top and left and then add it to the value at the same position in the original array. As shown in the figure below , Athere are 2three choices, choose the smaller value.

23.png

25.png

  • By analogy, the values ​​of all remaining positions can be obtained. As shown in the image below, the red numbers indicate the final selection.

26.png

  • Finally, the sum of the shortest paths from the starting point to the destination is drawn.

24.png

4.2 Encoding implementation

#include <iostream>
#include <cstring>
using namespace std;

//原始数组
int nums[3][3]= { {2,4,3},
	{7,6,2},
	{1,8,5},
};
//二维动态数组
int dp[3][3]= { {0,0,0},
	{0,0,0},
	{0,0,0},
};

int main(int argcwfh, char** argv) {

	//起始位置的路径路径是本身
	dp[0][0]=nums[0][0];
	//第一行值只受左边值影响
	for(int i=1; i<3; i++ ) {
		dp[0][i]=dp[0][i-1]+nums[0][i];
	}
	//第一列值只受上边影响
	for(int i=1; i<3; i++ ) {
		dp[i][0]=dp[i-1][0]+nums[i-1][0];
	}

	//由上向下
	for(int i=1; i<3; i++) {
		for(int j=1; j<3; j++ ) {
			dp[i][j]=nums[i-1][j-1]+min(dp[i][j-1],dp[i-1][j]);
		}
	}

	for(int i=0; i<3; i++) {
		for(int j=0; j<3; j++ ) {
			cout<<dp[i][j]<<"\t";
		}
		cout<<endl;
	}

	return 0;
}

5. Summary

Recursion and dynamic programming are the two major swordsmen in the algorithm world. They communicate with each other. When solving the same problem, one stands in the positive direction of the problem domain and the other stands in the opposite direction of the problem domain. The flexible use and mastery of these two algorithms is a compulsory path to the algorithm world.

Guess you like

Origin blog.csdn.net/y6123236/article/details/132005756