线性DP专题


1.最长公共子序列


给定两个长度分别为 N和M 的字符串 A 和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。

输入格式

第一行包含两个整数 N 和 M。

第二行包含一个长度为 N 的字符串,表示字符串 A。

第三行包含一个长度为 M 的字符串,表示字符串 B。

字符串均由小写字母构成。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N,M≤1000

输入样例:

4 5
acbd
abedc

输出样例:

3

一、可以用二维dp,dp[i] [j] 表示在第一个序列的第i位到第二个序列的j位的最长公共子串的长度。

二、二维数组要想给每一个赋值,即需要从底往上遍历。

三、讲遇到的情况分成两大类,一类是相等,一类是不等。

即状态转移方程我为

if(a[i] == b[j])
dp[i][j] = dp[i-1][j-1] + 1 ;
else if(a[i] != b[j])
dp[i][j] = max(dp[i-1][j] , dp[i][j-1]) ;

具体代码如下:

#include 
using namespace std ;

const int N = 1010 ;

int f[N][N] ;

char a[N], b[N] ;
int main()
{
    int n , m ; 
    cin >>n >> m  ;
    for(int i = 1 ; i <= n ;++i)
        cin >> a[i] ; 
    for(int j = 1 ; j <= m ;++j)
        cin >> b[j] ;
        
    for(int i = 1 ; i <= n ; ++i)
    {
        for(int j = 1 ; j <= m ; ++j)
        {
            if(a[i] == b[j])
            {
                f[i][j] = f[i-1][j-1] +1 ;
            }
            else
            {
                f[i][j] = max(f[i-1][j] , f[i][j-1]) ;
            }
        }
        
    }
            cout << f[n][m] <

二、最长上升子序列

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000
−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

数据范围给的比较大,所以直接暴力dp就可以了。

一、创建一个一维的dp数组,dp[i]表示的是从1到i的字串中, 以q[i]所结尾的子序列的最长子序列的长度.

二、dp思路,搜索数组中目标值前面的值,找到严格比target小的index, 将本target接在后面即可。然后一直比较,将最大的值赋给dp即可。

三、最终的结果不一定是最后的dp结果,而是过程中出现最大的值,因此需要在每一个小循环结束的时候,设置一个res,专门用来记录出现的最大的dp值。

代码如下:

#include 
using namespace std ;

const int N = 1010 ;

int w[N] ;
int f[N] ; 
int main()
{
    int n ; 
    cin >>n ;
    
    
    int res =0  ;
    for(int i = 1 ; i <= n ;++i)
    {
        cin >> w[i] ;
    }
    for(int i = 1 ; i <= n ;++i)
    {
        f[i] = 1 ;
        for(int j = 1 ; j <= i ;++j)
        {
            if(w[i] > w[j]) f[i] = max(f[i] , f[j] +1) ;
        }
        res = max(res , f[i]) ;
    }
    
    cout << res <

三、最长上升子序列II

题目同二,但是数据范围从1000改到了1e5。

所以,直接把暴力给毙了.

我们想到最多可以用nlogn来优化,于是可以用二分来优化.

dp是用不了了,可以用贪心加二分。

具体思路:

一、对于原数组中的值,依次在二分数组中找到小于他的最大值,二分数组的作用是一边存储,一边用于搜索。

二、最后返回q数组的长度即可。

#include 

using namespace std ;

const int N = 100010 ;

int w[N] ;
int q[N] ;
int main()
{
    int n ; 
    cin >> n ;
    q[0] = -2e9 ; 
    
    for(int i = 1 ; i <=  n ;++i)
    {
        cin >> w[i] ;
    }
    
    int len = 0  ;
    for(int i = 1 ; i <= n ;++i)
    {
        int left = 0 ; int right = len ;
        while(left < right)
        {
            int mid = (left + right +1) >> 1 ;
            if(q[mid] < w[i])
            {
                left = mid  ; 
            }
            else 
            {
                right = mid - 1  ; 
            }
        }
        q[right+1] = w[i] ;
        len = max(len , right+1) ;
    }
    cout << len <

文章作者: 罗林
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗林 !
  目录