动态规划-最长公共上升子序列-n^2解法
1. 题目描述
给定两个数列\(A, B\),如果他们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列。求\(A\)和\(B\)的最长公共上升子序列。
输入格式
第一行包含一个整数\(N\),表示\(A\)和\(B\)的长度。
第二行包含\(N\)个整数,表示数列\(A\)。
第三行包含\(N\)个整数,表示数列\(B\)。
输出格式
输出一个整数,表示最长公共上升子序列的长度。
数据范围
\(1 ≤ N≤ 3000\),序列中的数字均不超过\(2^{31} - 1\).
输入样例
4
2 2 1 3
2 1 2 3
输出样例
2
2. 朴素解法\(O(n^3)\)
朴素解法将最长公共子序列和最长上升子序列模型相结合。
用\(f[i][j]\)表示第一个序列的前\(i\)个数,和第二个序列的前\(j\)个数,且最后一个数为\(b[j]\)的最长公共上升子序列的长度。
则可以根据\(a[i]\)是否等于\(b[j]\)进行分类讨论。
\begin{array}{rcl}
f[i - 1][j] & & {a[i] != a[j]}\\
max_{k < j且b[k] < b[j]}(f[i][j], f[i- 1][k]) & & {a[i] == b[j] }\\
\end{array} \right.
\]
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3010;
int a[N], b[N];
int 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 ++)
{
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], 1);
for(int k = 1; k < j; k ++)
if(b[k] < b[j]) f[i][j] = max(f[i][j], f[i - 1][k] + 1);
}
}
}
int res = 0;
for(int i = 1;i <= n; i ++) res = max(res, f[n][i]);
cout << res << endl;
return 0;
}
3. 代码优化\(O(n^2)\)
上述朴素做法的时间复杂度为\(O(n^3)\)。那么我们如何优化呢?
观察代码的最内层循环部分
if(a[i] == b[j])
{
f[i][j] = max(f[i][j], 1);
for(int k = 1; k < j; k ++)
if(b[k] < b[j]) f[i][j] = max(f[i][j], f[i - 1][k] + 1);
}
由于执行最内层循环的时候
a[i] == b[j]
。所以最内层条件可以变为b[k] < a[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])
{
f[i][j] = max(f[i][j], 1);
for(int k = 1; k < j; k ++)
if(b[k] < a[i]) f[i][j] = max(f[i][j], f[i - 1][k] + 1);
}
}
}
我们实际上就是在求:当第一个循环走到
i
,第二个循环走到j
且满足"a[i] > b[k]
且k < j
"时f[i - 1][j]
的最大值。也就是说当我们访问到第
j
层循环的时候,需要前j-1
层的数据。因此,可以在j
的循环中迭代求解。代码如下
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(b[j] < a[i]) maxv = max(maxv, f[i - 1][j] + 1); // maxv的更新一定要在f[i][j]的更新之后。
}
}
4. 总结
- 本题是最长公共子序列模型和最长上升子序列模型的结合版, 我们可以根据最长上升子序列和最长公共子序列的状态表示方法得到启发,构建状态表示方法和状态转移方程。
- 朴素解法的时间复杂度较高,但是我们可以根据代码进行优化,将时间复杂度由\(O(n^3)\)降到\(O(n^2)\)。
动态规划-最长公共上升子序列-n^2解法的更多相关文章
- 动态规划——最长公共上升子序列LCIS
问题 给定两个序列A和B,序列的子序列是指按照索引逐渐增加的顺序,从原序列中取出若干个数形成的一个子集,若子序列的数值大小是逐渐递增的则为上升子序列,若A和B取出的两个子序列A1和B1是相同的,则A1 ...
- [ACM_动态规划] UVA 12511 Virus [最长公共递增子序列 LCIS 动态规划]
Virus We have a log file, which is a sequence of recorded events. Naturally, the timestamps are s ...
- codevs 2185 最长公共上升子序列
题目链接: codevs 2185 最长公共上升子序列codevs 1408 最长公共子序列 题目描述 Description熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目.小沐沐先让奶牛研究了最长上升 ...
- HDOJ 1423 Greatest Common Increasing Subsequence 【DP】【最长公共上升子序列】
HDOJ 1423 Greatest Common Increasing Subsequence [DP][最长公共上升子序列] Time Limit: 2000/1000 MS (Java/Othe ...
- HDU 4512 最长公共上升子序列
各种序列复习: (1)最长上升子序列. 1.这个问题用动态规划就很好解决了,设dp[i]是以第i个数字结尾的上升子序列的最长长度.那么方程可以是dp[i]=max(dp[j]+1).(j<i). ...
- LCIS 最长公共上升子序列问题DP算法及优化
一. 知识简介 学习 LCIS 的预备知识: 动态规划基本思想, LCS, LIS 经典问题:给出有 n 个元素的数组 a[] , m 个元素的数组 b[] ,求出它们的最长上升公共子序列的长度. 例 ...
- hdu 1423 最长公共递增子序列 LCIS
最长公共上升子序列(LCIS)的O(n^2)算法 预备知识:动态规划的基本思想,LCS,LIS. 问题:字符串a,字符串b,求a和b的LCIS(最长公共上升子序列). 首先我们可以看到,这个问题具有相 ...
- 最长公共上升子序列(codevs 2185)
题目描述 Description 熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目.小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们要研究最长公共上升子序列了. 小沐沐说,对 ...
- 最长公共上升子序列(LCIS)
最长公共上升子序列慕名而知是两个字符串a,b的最长公共递增序列,不一定非得是连续的.刚开始看到的时候想的是先用求最长公共子序列,然后再从其中找到最长递增子序列,可是仔细想一想觉得这样有点不妥,然后从网 ...
随机推荐
- python 迭代器 iter多次消费
问题 Python 中的迭代器是我们经常使用的迭代工具, 但其只能消费一次,再次消费便会出现 StopIteration 报错. 解决方案 封装了一个类,当迭代器使用完后再次初始化. 代码 class ...
- 数据库之postgreSQL入门操作指南
一.增 二.删 三.改 四.查 五.SQL操作表 1.增加列 ALTER TABLE table_name ADD column_name datatype; 2.删除一列 ALTER TABLE t ...
- P类问题,NP,NPC,HPHard,coNP,NPI问题 的简单认识
参考<算法设计技巧与分析>--沙特 问题可以分为判定类问题和最优化问题,判定类问题可以转化为最优化问题,所以下面讨论的是判定类的问题. P类问题是可以在多项式时间 采用确定性算法给出解 ...
- Linux 网络协议栈开发基础篇—— 网桥br0
一.桥接的概念 简单来说,桥接就是把一台机器上的若干个网络接口"连接"起来.其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去.以使得网口之间的报文能够互相转发. 交换机 ...
- Spring-cloud-netflix-hystrix
服务注册中心eureka-server已经搭好,并且SPRING-CLOUD-NETFLIX-EUREKA-CLIENT-APPLICATION提供一个hello服务 畏怯还编写一个eureka-cl ...
- sdut3562-求字典序最小的最短路 按顶点排序后spfa的反例
首先我们可以这么搞...倒序建图,算出源点s附近的点距离终点的距离,然后判断一下,终点是否能跑到源点 能跑到的话呢,我们就判断s周围的点是否在最短路上,然后我们选编号最小的点就好了 代码 #inclu ...
- Linux下开发STM32单片机
一开始学习51单片机就是用的MDK这个IDE软件,IDE软件虽然看起来直观好像更加容易入门(因为有界面看起来很形象),但是实际上IDE却是向我们这些入门人员隐藏了背后真实存在的过程,让我们以为编译就是 ...
- μC/OS-III---I笔记4---软件定时器
软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器.他和时间管理共同组成了系统的时间管理大部分的内容.系统一开始的系统初始化函数OSInit函数内调用了 ...
- Flutter & App
Flutter & App Android & iOS https://flutter.dev/docs/deployment/flavors https://flutter.dev/ ...
- Taro 版本
Taro 版本 https://taro-docs.jd.com/taro/versions.html 1.x 1.3.34 https://taro-docs.jd.com/taro/docs/1. ...