LCIS最长公共上升子序列

目录

  • 一、题目描述
  • 二、基本思路
    • 1.明确dp数组定义
    • 2.分解集合
    • 3.画图分析
  • 三、C++代码

一、题目描述

给定一个长度为 N 数组 a 和一个长度为 M 的数组 b。
请你求出它们的最长公共上升子序列长度为多少。
输入第一行包含两个整数 N,M,分别表示数组 a 和 b 的长度。第二行包含 N 个整数 a 1 , a 2 , … , a n a_{1}, a_{2}, \ldots, a_{n} a1,a2,,an。第三行包含
M 个整数 b 1 , b 2 , … , b n b_{1}, b_{2}, \ldots, b_{n} b1,b2,,bn。输出一行整数表示答案。

二、基本思路

本题为经典的线性动态规划问题。

1.明确dp数组定义

先回顾一下LIS和LCS:
LIS最长上升子序列:dp[i]定义为以a[i]为结尾的最长上升子序列长度。
LCS最长公共子序列:dp[i][j]定义为数组a和数组b的所有公共子序列长度值的最大值。
LCIS最长公共上升子序列:dp[i][j]定义为数组a和数组b的所有公共子序列中以b[j]结尾的上升子序列长度值的最大值。

2.分解集合

首先依据公共子序列中是否包含a[i],将dp[i][j]所代表的集合划分成两个不重不漏的子集:
1.不包含a[i]的子集,也就是说没有出现公共子集,有没有这个a[i]不影响当前a[i],b[j]对应的dp值,则其dp值继承自a[i-1],b[j]所对应dp值。
则状态转移方程为dp[i][j]=dp[i-1][j]
2.包含a[i]的子集,a[i]=b[j]即在a[i]状态出现了一个公共子集,则将这个子集继续划分:即只需要知道在没有a[i]的上一个状态a[i-1]时,b[1~j-1]的最大的dp值+1即可。为了保证单调递增,当前公共子集a[i]=b[j]必须大于前面任何公共子集,也就是在每一次i递增中,a[i]b[j]要大于a[i-1]状态的任一b[1~j-1]时才能+1。
则状态转移方程为dp[i][j] = max(dp[i][j], dp[i-1][k]+1)

3.画图分析

LCIS最长公共上升子序列_第1张图片
红色箭头表示出现公共子集,并计算dp,绿色箭头表示继承自上一个a[i]状态的dp。注意i=1、2时,并没有出现数字4、5的公共子集,所以它们的dp为0。

三、C++代码

void solve(int N, int M) {
	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] < b[j]) {
						dp[i][j] = max(dp[i][j], dp[i - 1][k] + 1);
					}
				}
			}
			else {
				dp[i][j] = dp[i - 1][j];
			}
		}
	}
}

注意求出的dp[i][j]是以b[j]结尾的上升子序列长度值的最大值,所以要得到所有以b[1~j]结尾的dp值的最大值,需要遍历一下求最大值。

	for (int i = 1; i <= M; i++) {
		res = max(res, dp[N][i]);
	}

本文参考来源:
AcWing 272. 最长公共上升子序列
蓝桥题库LCIS

你可能感兴趣的:(C++,算法,c++,动态规划)