Linear DP of Dynamic Programming

The so-called linear DP means that our recurrence equation has an obvious linear relationship. Each state in dynamic programming is a multi-dimensional state, and the multi-dimensional state has a solution order. For example, the knapsack problem is a two-dimensional problem, and it is calculated line by line. Such DPs are called linear DPs.

Digital triangle

Problem Description

Given a digital triangle as shown in the figure below, starting from the top, at each node, you can choose to move to the node on the lower left or to the node on the lower right, and walk to the bottom, asking for a path To maximize the sum of the numbers on the path.

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5

Input format The
first line contains the integer n, which represents the number of layers of the digital triangle.

In the next n rows, each row contains several integers, where the i-th row represents the integer contained in the i-th layer of the digital triangle.

Output format
Output an integer, which represents the largest path number sum.

Data range
1 ≤ n ≤ 500,
−10000 ≤ integer in triangle ≤ 10000

Input sample:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

Sample output:

30

Problem-solving ideas:

Insert picture description here
Through the above analysis, we conclude that the state transition equation is the maximum value of i-1,j-1 from the upper left and the maximum i-1,j from the upper right plus the value of the position.
Dynamic programming time complexity: the number of states is multiplied by the calculation amount of the transition. The total time complexity here is O(n2).
Here we do it from the bottom up, so at the very beginning we initialize the boundary problem. That is, the sum of the numbers in the last line is already the value at that position. Then go up in turn, then the value at the last 0, 0 position is the maximum sum of our digital triangle. If it is from top to bottom, then the position of the last line should also consider which one is the maximum. Obviously, the code will be a bit more concise if you traverse from the bottom up.

#include<algorithm>
#include<iostream>
using namespace std;
#define N 1010
int n;
int tri[N][N];
int f[N];
int main(){
    
    
    cin>>n;
    //读入数字三角形
    for(int i=0;i<n;i++){
    
    
        for(int j=0;j<=i;j++){
    
    
            cin>>tri[i][j];
        }
    }
    //处理边界问题,注意,这里使用到了滚动数组
    //我们从最后一行开始做
    for(int i=0;i<n;i++){
    
    
        f[i]=tri[n-1][i];
    }
    //状态转移方程
    for(int i=n-2;i>=0;i--){
    
    
        for(int j=0;j<=i;j++){
    
    
            f[j]=tri[i][j]+max(f[j],f[j+1]);
        }
    }
    cout<<f[0];
    return 0;
    
}

Longest ascending subsequence

Problem Description

Given a sequence of length N, find the longest length of the subsequence whose value is strictly monotonically increasing.

Input format The
first line contains the integer N.

The second line contains N integers, representing the complete sequence.

Output format
Output an integer, indicating the maximum length.

Data range
1≤N≤1000,
−109≤Number in the sequence≤109
Input example:
7
3 1 2 1 8 5 6
Output example:
4
Difficulty: Simple
Time/space limit: 1s / 64MB
Total number of passes: 9936
Total attempts: 15166

Problem-solving ideas:

The longest ascending subsequence in 3 1 2 1 8 5 6 is 1256,
which represents the dimension here. We must adhere to a principle to allow the answer to be derived. On this basis, the less dimension we have, the better. We generally are Consider the situation from small to large.
Insert picture description here
The number of states is O(N), and the time complexity of transfer is O(N), so the time complexity is O(N2)

#include<iostream>
#include<algorithm>
using namespace std;
#define N 1010
int arr[N];
int n;
int maxLen[N];
int main(){
    
    
    cin>>n;
    //初始化最长上升子序列为1
    for(int i=0;i<n;i++){
    
    
        cin>>arr[i];
        maxLen[i]=1;        //只有a[i]一个数的情况
    }
    //从前往后处理每一个状态
    for(int i=1;i<n;i++){
    
    
        for(int j=i-1;j>=0;j--){
    
    
            //只有aj<ai的时候,我们才计算最长上升子序列
            if(arr[i]>arr[j]){
    
    
                maxLen[i]=max(maxLen[i],maxLen[j]+1);
            }
        }
    }
    int res;
    //最后选取一个最长上升子序列
    for(int i=0;i<n;i++){
    
    
        res=max(res,maxLen[i]);
    }
    cout<<res;
    return 0;
}

Longest common subsequence

Problem Description

Given two strings text1 and text2, return the length of the longest common subsequence of these two strings.

A subsequence of a character string refers to a new character string: it is a new character string formed by deleting certain characters (or no characters) from the original character string without changing the relative order of the characters.
For example, "ace" is a subsequence of "abcde", but "aec" is not a subsequence of "abcde". The "common subsequence" of two strings is the subsequence shared by the two strings.

If the two strings have no common subsequence, 0 is returned.
Example 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace",它的长度为 3

Example 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3

Example 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0

Problem-solving ideas

The longest common subsequence (Longest Common Subsequence, referred to as LCS) is a very classic interview question, because its solution is a typical two-dimensional dynamic programming, most of the more difficult string problems are the same as this question, for example Edit distance. Moreover, this algorithm can be used to solve other problems with a little modification, so the LCS algorithm is worth mastering.

The so-called subsequence is to keep the original order, but it can be discontinuous. After reviewing the questions, you may have questions. Why is this problem solved by dynamic programming? Because of the problem of sub-sequence types, it is not easy to enumerate all possible results, and what the dynamic programming algorithm does is exhaustion + pruning, they are born a pair. So it can be said that as long as the problem of subsequence is involved, dynamic programming is needed to solve it in all likelihood.

The first step is to clarify the meaning of the dp array.

For the dynamic programming problem of two strings, the routine is universal.

For example, for the strings s1 and s2, their lengths are m and n respectively. Generally speaking, a DP table like this must be constructed: int[][] dp = new int[m+1][n+1].

Why add 1 here? The reason is that you can not add 1, but if you do not add 1, you will use other restrictions to ensure that the index is valid, and when you add 1, you don’t need to judge and just let the index be 0 The rows and columns of represents an empty string.

The second step is to define the boundary conditions.
We specifically let the rows and columns with index 0 represent empty strings. Both dp[0][…] and dp[…][0] should be initialized to 0. This is the base case.

The third step is to find the state transition equation.
This is the most difficult step in dynamic programming. Let's derive it through a case.

For text1: abcde and text2: ace two strings, we define two pointers to traverse i and j.

The length of traversal text1 is m, and the pointer i is defined from 0 to m. Fix the position of the i pointer (i == 1), then start to traverse the length of text2 as n, define the pointer j, from 0 to n.
Insert picture description here
The first traversal i = 1, j = 1, the two as are the same, so dp[1][1] = 1 The
second traversal i = 1, j = 2, a and c are not equal, and cannot be 0, here It needs to be converted into the longest subsequence of a and ac. Here, the previous relationship needs to be passed over, so dp[1][2] = 1 and the
third traversal i = 1, j = 3, a and e are not the same, change the previous The relationship is passed over, so dp[1][3] = 1
text2: ace has completed the first round, and then text1: abcde comes to the b character.

The fourth traversal i = 2, j = 1, is to compare the longest substring of ab and a, and pass the previous relationship, so dp[2][1] = 1
and so on... (see the figure above for details)

We will find that when traversing two strings of characters, we need to consider the values ​​before the two-level traversal (relation transfer) when they are different, that is, the larger value of the left and upper sides. When you want the same, you need to consider that each does not contain the current string The length of the subsequence, plus 1.

Therefore, it can be concluded
that the two characters that are being compared are not the same, then we have to take its "either text1 forward one space, or text2 forward one space, the maximum of the two"
dp[i + 1][j + 1] = Math.max(dp[i+1][j], dp[i][j+1]);

The two characters to be compared are the same, just go to the value of one space before them and add 1 to them: dp[i+1][j+1] = dp[i][j] + 1;

class Solution {
    
    
    public int longestCommonSubsequence(String text1, String text2) {
    
    
        int m=text1.length(),n=text2.length();
        int [][] cs=new int[m+1][n+1];
        //设置边界条件
        for(int i=0;i<=m;i++){
    
    
            cs[i][0]=0;
        }       
        for(int i=0;i<=n;i++){
    
    
            cs[0][i]=0;
        }
        for(int i=1;i<=m;i++){
    
    
            for(int j=1;j<=n;j++){
    
    
                //状态转移方程
                if(text1.charAt(i-1)==text2.charAt(j-1)){
    
    
                    cs[i][j]=cs[i-1][j-1]+1;
                }else{
    
    
                    cs[i][j]=Math.max(cs[i-1][j],cs[i][j-1]);
                }
            }
        }
        return cs[m][n];
    }
}

Guess you like

Origin blog.csdn.net/qq_39736597/article/details/114261553