题目描述

时间限制:1.0s 内存限制:256.0MB

问题描述

   给定一个长为\(n\)的序列,求它的最长上升子序列的长度。

输入格式

   输入第一行包含一个整数\(n\)。

   第二行包含\(n\)个整数\(a_1,a_2,…,a_n\),为给定的序列。

输出格式

   输出一个非负整数,表示最长上升子序列的长度。

样例输入

 5
1 3 2 5 4

样例输出

 3

数据规模和约定

   \(0<n\leq10^5\),每个数不超过\(10^6\)。

解析

   所谓最长上升子序列,就是给定一列数,求序列中严格上升的子序列,子序列中数的位置不一定连续。

1.动态规划dp

   这是动态规划中的一个经典应用题目,动态规划的思想就是把问题分解为一些本质上还是相同问题的小问题,这些小问题的区别仅在于输入的参数不同,而每一个小问题的解都可以由比他参数更小的一些小问题的解推出,最小的小问题有显而易见的解,这被称之为边界。最长上升子序列(LIS)问题也是这样。

   \(a_i\)(\(1\leq i<n\))表示序列中第\(i\)个整数

   \(dp_i\) 表示以\(a_i\)为结尾的最长上升子序列的长度

   于是,\(dp_i\)要把\(a_i\)加到以\(a_j\)(\(1\leq j<i\))为上升子序列末尾的后面,并且符合\(a_j<a_i\),找一个最大的dp[j],然后令\(dp_i = dp_j + 1\)即求出了\(dp_i\)。

   最后在所有的\(dp_i\)中取一个最大的即为整个序列的最长上升子序列的长度。

   算法时间复杂度为\(O(n^2)\),当范围大一些的时候是有些低效的。

#include <iostream>
#include <algorithm>
using namespace std;
int a[100005], d[100006];
const int INF = 0x3f3f3f3f;
int main() {
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int maxx = -INF;
for (int i = 1; i <= n; i++) {
d[i] = 1;
for (int j = 1; j < i; j++)
if (a[j] < a[i])
d[i] = max(d[i], d[j] + 1);
maxx = max(maxx, d[i]);
}
cout << maxx << endl;
return 0;
}
2.二分搜索优化

   \(a_i\)(\(1\leq i<n\))表示序列中第\(i\)个整数

   \(dp_m\) 表示长度为\(m\)的最长公共子序列的末尾最小值

   当\(i=1\)时,\(dp_i=a_i\)。

   当\(i\)逐渐增加,每次扫过\(a_i\),因为\(dp_m\)代表长度为i的最长上升子序列的最小末尾,这可以代表一个长度为m的最长上升子序列,我们考虑可以把\(a_i\)加到哪个最长上升子序列后面构成一个长度\(+1\)的新的最长上升子序列。那么我们就可以在\(dp\)数组中寻找到\(j\)(\(1\leq j\leq m\))并且\(dp_j<a_i\leq dp_{j+1}\),把\(a_i\)加到这个数代表的序列后面:\(dp_{j+1}=a_i\)。

   因为\(dp\)数组是有序的,所以在进行寻找的时候就可以用二分搜索来进行。而当我们把\(a\)数组全部扫描完之后,\(dp\)数组中的最后一个有效的数字的位置即是我们所求的最长上升子序列的长度\(m\)。

   由于循环中的搜索变成了二分搜索,时间复杂度为\(O(log_n)\),所以整体时间复杂度是\(O(nlogn)\)。

#include <iostream>
#include <algorithm>
using namespace std;
int dp[100005], a[100005];
int b_search(int x, int l, int r) {
while (l < r) {
int mid = (r + l) / 2;
if (dp[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
int main(int argc, char** argv) {
int n; cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dp[i] = 0x3f3f3f3f;
}
int m = 1;
dp[1] = a[1];
for (int i = 2; i <= n; i++) {
if (a[i] > dp[m]) dp[++m] = a[i];
else {
int j = b_search(a[i], 1, m);
dp[j] = min(dp[j], a[i]);
}
}
cout << m << endl;
return 0;
}

   在第二个算法中我们可以通过在二分查找中改变“上确界”和“下确界”,以及在第一个算法中通过改变符号(“<”和“<=”或“>”、“>=”等),求出最长不下降、不上升、严格下降子序列等问题。

最长公共子序列-LIS的更多相关文章

  1. 最长公共子序列(LCS)和最长递增子序列(LIS)的求解

    一.最长公共子序列 经典的动态规划问题,大概的陈述如下: 给定两个序列a1,a2,a3,a4,a5,a6......和b1,b2,b3,b4,b5,b6.......,要求这样的序列使得c同时是这两个 ...

  2. 算法设计 - LCS 最长公共子序列&&最长公共子串 &&LIS 最长递增子序列

    出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的 ...

  3. 最长递增子序列(lis)最长公共子序列(lcs) 最长公共上升子序列(lics)

    lis: 复杂度nlgn #include<iostream> #include<cstdio> using namespace std; ],lis[],res=; int ...

  4. 最长公共子序列-LCS问题 (LCS与LIS在特殊条件下的转换) [洛谷1439]

    题目描述 给出1-n的两个排列P1和P2,求它们的最长公共子序列. 输入 第一行是一个数n, 接下来两行,每行为n个数,为自然数1-n的一个排列. 输出 一个数,即最长公共子序列的长度 输入样例 5 ...

  5. 最长上升子序列(LIS)与最长公共子序列(LCS)

    1.LIS : 给定一个序列,求它的最长上升子序列(n<=2000) 第一种 O(n^2): dp[i] 为以i为开头的最长上升子序列长度 code1: #include<cstdio&g ...

  6. 最长连续公共子序列(LCS)与最长递增公共子序列(LIS)

    最长公共子序列(不连续) 实际问题中也有比较多的应用,比如,论文查重这种,就是很实际的一个使用方面. 这个应该是最常见的一种了,不再赘述,直接按照转移方程来进行: 按最普通的方式就是,直接构造二维矩阵 ...

  7. 最长公共子序列(LCS)、最长递增子序列(LIS)、最长递增公共子序列(LICS)

    最长公共子序列(LCS) [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列.令给定的字 ...

  8. 【线型DP模板】最上上升子序列(LIS),最长公共子序列(LCS),最长公共上升子序列(LCIS)

    BEGIN LIS: 一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序 ...

  9. 【LCS,LIS】最长公共子序列、单调递增最长子序列

    单调递增最长子序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列就是abdf,长度为4   输入 ...

随机推荐

  1. 文件挂载(一)- Linux挂载Linux文件夹

    一.概述 工作中经常会出现不同服务器.不同操作系统之间文件夹互相挂载的情形,例如文件服务器或数据备份服务器. 挂载一般来说就是以下四种类型: 同类型操作系统 a. linux挂载linux文件夹 b. ...

  2. spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象

    相关 知识 >>> 相关 练习 >>> 实现要求: 在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXm ...

  3. 使用 Android Studio 开发工具创建一个 Android 应用程序,显示一行文字“Hello Android”,并将应用程序的名称更改为“FirstApp”。

    需求说明: 使用 Android Studio 开发工具创建一个 Android 应用程序,显示一行文字"Hello Android",并将应用程序的名称更改为"Firs ...

  4. Java基础周测题,获取用户从控制台输入的年份,判断是否是闰年

    需求说明: 获取用户从控制台输入的年份,判断是否是闰年: 是闰年: 是平年: 实现代码: import java.util.Scanner; public class test { public st ...

  5. Log4j2完整XML参考(详细注释说明)

    1.说明 本文提供完整的log4j2.xml配置文件, 供开发中参考使用,可以作为模板, 配置对应实现如下常用的功能: 1.自动检测和重新加载配置,每10分钟(600s)检测一次 2.每个日志文件最大 ...

  6. Echart可视化学习(九)

    文档的源代码地址,需要的下载就可以了(访问密码:7567) https://url56.ctfile.com/f/34653256-527823386-04154f 官网找到类似实例, 适当分析,并且 ...

  7. springboot集成oss阿里云存储

    一.注册阿里云 二.购买OSS 三.创建桶 设定权限,其它默认即可 四.创建目录 点击桶名,进入创建目录即可. 五.开发文档 引入依赖: <dependency> <groupId& ...

  8. Yum安装Maven

    一.安装 wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum. ...

  9. HDU-2032.杨辉三角(C语言描述)

    Problem Description 还记得中学时候学过的杨辉三角吗?具体的定义这里不再描述,你可以参考以下的图形: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 ...

  10. mysql按照时间分组查询

    mysql 按年.月.周.日分组查询 1.按照年份分组查询 SELECT DATE_FORMAT(t.bill_time,'%Y') month_time,sum(t.pay_price) total ...