ACWING 272 最长公共上升子序列

https://www.acwing.com/problem/content/274/

题意

给定两个序列数列 A A A B B B,求它们的最长公共上升子序列。

公共上升子序列的定义是可在 A A A B B B元素集合交集中保序选取一部分构成新的序列,该序列是严格递增的。

思路

考虑最长上升子序列(LIS)和最长公共子序列(LCS)的转移方程和定义

  1. LIS f [ i ] = m a x 0 ≤ j < i , A [ j ] < A [ i ] f [ j ] + 1 f[i]=max_{0 \leq j <i,A[j]<A[i]}{f[j]+1} f[i]=max0j<i,A[j]<A[i]f[j]+1,f[i]代表A[1]~A[i]构成的LIS长度
  2. LCS:如 A [ i ] ! = B [ j ] A[i]!=B[j] A[i]!=B[j],则 f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i ] [ i − 1 ] ) f[i][j]=max(f[i-1][j],f[i][i-1]) f[i][j]=max(f[i1][j],f[i][i1]),否则 f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] , f [ i − 1 ] [ j − 1 ] + 1 ) f[i][j]=max(f[i-1][j],f[i][j-1],f[i-1][j-1]+1) f[i][j]=max(f[i1][j],f[i][j1],f[i1][j1]+1),f[i][j]代表A[1]~A[i]B[1]~B[i]构成的LCS的长度

我们可以如此定义最长公共上升子序列(LCIS)的转移方程

A [ i ] ! = B [ j ] A[i]!=B[j] A[i]!=B[j]
f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j]

否则

f [ i ] [ j ] = m a x 0 ≤ k < j , B [ k ] < B [ j ] f [ i − 1 ] [ k ] + 1 = m a x 0 ≤ k < j , B [ k ] < A [ i ] f [ i − 1 ] [ k ] + 1 \begin{align} f[i][j]&= max_{0 \leq k < j,B[k]<B[j]}{f[i-1][k]+1}\\ &=max_{0 \leq k < j,B[k]<A[i]}{f[i-1][k]+1} \end{align} f[i][j]=max0k<j,B[k]<B[j]f[i1][k]+1=max0k<j,B[k]<A[i]f[i1][k]+1

其中 f[i][j]表示A[1]~A[i]B[1]~B[i]构成的LCIS的长度

这可由一个三重循环来实现

for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
        if(a[i]==b[j]){
            for(int k=0;k<j;k++){
                if(b[k]<a[i]){
                    f[i][j]=max(f[i][j],f[i-1][k]+1);
                }
            }
        }
        else f[i][j]=f[i-1][j];//a[i]!=b[j]
    }
}

注意到if(b[k]==a[i])j循环的内部,在j1m变化的过程中a[i]都是不变的,因此实际上在每一次j的循环和自增中,我们只需要单独考虑k所增加的一个新情况,即k=j-1的情况即可。

由此,我们可以将

 for(int k=0;k<j;k++)
    if(b[k]<a[i])
        f[i][j]=max(f[i][j],f[i-1][k]+1);

这一部分做出优化,提取到j循环的外部,得到 O ( n 2 ) O(n^2) O(n2)的实现

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

using namespace std;
int n, m;
const int N = 3010;
int a[N], b[N];
int f[N][N];

int main() {
  cin >> n;
  for (int i = 1; i <= n; i++)
    cin >> a[i];
  for (int i = 1; i <= n; i++)
    cin >> 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]);
  cout << res << endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值