【最长公共上升子序列】

题目

题目地址

解题思路

闫氏DP分析法:

状态表示 f[i][j]—集合:

考虑 a 中前 i 个数字,b 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案

状态表示 f[i][j]—属性:

该方案的子序列长度最大 max

状态转移 :
1.a[i]!=b[j]

从考虑 a数组 中前 i-1 个数字, b数组 中前 j 个数字 ,且当前以 b[j] 结尾的子序列的方案转移过来:

f i , j = m a x ( f i , j   f i − 1 , j ) f_{i,j}=max(f_{i,j}\,f_{i−1,j}) fi,j=max(fi,jfi1,j)

2.a[i]==b[j]

从考虑 a数组 中前 i 个数字, b数组 中前 k 个数字 ,且当前以 b[k] 结尾的子序列的方案转移过来:
f i , j = m a x ( f i , j , f i − 1 , k + 1 )   k ∈ [ 0 , j − 1 ] , b j > b k fi,j=max(f_{i,j},f_{i−1,k}+1) \ k∈[0,j−1],b_j>b_k fi,j=max(fi,j,fi1,k+1) k[0,j1],bj>bk
初始状态:f[0][0]
目标状态:f[n][i]

如果按照上面思路实现需要三重循环
 for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= n; ++ j)
        {
            f[i][j] = f[i - 1][j];
            if (a[i] == b[j])
            {
                for (int k = 0; k < j; ++ k)
                {
                    if (b[j] > b[k])
                    {
                        f[i][j] = max(f[i][j], f[i - 1][k] + 1);
                    }
                }
            }
        }
    }


然后我们发现对于第二种状态转移是找到 0 ~ j -1之间,值比b[j]小的最大值,而我们发现每次出现一个新的j时,都从头开算是重复运算(因为本层内i还没有变化),不划算。那怎么才能优化呢?那就是在本层循环中用一个变量记录比a[i]小的b[k]最大值,这样就不用每次现算了,直接拿来就用了

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 3010;
int f[N][N], a[N], b[N];
int n;
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);

    for (int i = 1; i <= n; i ++ ){
        int maxv = 1;
        for (int j = 1; j <= n; j ++ ){
            f[i][j] = f[i-1][j];
            
            if(a[i]==b[j])f[i][j] = max(f[i][j] , maxv);
            
            if(a[i] > b[j]) maxv = max(maxv, f[i-1][j] + 1);
        }
    }
    int res = 0;
    for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
    printf("%d\n", res);

    return 0;
}

如有错误,请指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值