基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题
收藏
关注
给出长度为N的数组,找出这个数组的最长递增子序列。(递增子序列是指,子序列的元素是递增的)

例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10。

 
Input
第1行:1个数N,N为序列的长度(2 <= N <= 50000)
第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)
Output
输出最长递增子序列的长度。
Input示例
8
5
1
6
8
2
4
5
10
Output示例
5
这个问题是被称作最长上升子序列的著名问题。解决方法有多种,这里介绍两种,
第一种是时间复杂度为O(n^2)的,不过这里我提交了,超时了,但是也简单介绍下,这种方法比较简单,只有我们知道DP,一般都可以想到,将dp[i]定义为以a[i]结尾的最长上升子序列的长度,而已a[i]结尾的上升子序列可以分为两种,一种是只包含a[i]的子序列,另一种是满足j<i并且a[j]<a[i]的以a[j]为结尾的上升子列末尾,尾加上a[i]后的得到的子序列,然后我们就可以得到递推关系dp[i]=max{1,dp[j]+1|j<i且a[j]<a[i]},接下来写代码就很简单了。
 #include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=;
int a[maxn];
int dp[maxn]; //dp[i]表示以a[i]为末尾的最长上升子序列长度
int n; int main()
{
cin>>n;
for(int i=;i<=n;i++) cin>>a[i];
int ans=;
for(int i=;i<=n;i++)
{
dp[i]=;
for(int j=;j<i;j++)
{
if(a[j]<a[i])
dp[i]=max(dp[i],dp[j]+);
}
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
return ;
}

而这题需要用的是第二种方法,就是看这篇博客里的:http://www.cnblogs.com/mengxm-lincf/archive/2011/07/12/2104745.html

还有这个ty大佬同学博客里的:https://www.cnblogs.com/tuyang1129/p/9282657.html

最长递增子序列,Longest Increasing Subsequence 下面我们简记为 LIS。
排序+LCS算法 以及 DP算法就忽略了,这两个太容易理解了。

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

 //最长上升子序列LIS
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=;
int n;
int dp[maxn]; //dp[i]表示整个序列中长度为i的最长子序列的末尾元素 最小值 int main()
{
cin>>n;
int num;
cin>>num;
dp[]=num;
int cnt=;
for(int i=;i<n;i++)
{
cin>>num;
if(num>dp[cnt-]) dp[cnt++]=num;
else
{
int p=lower_bound(dp,dp+cnt-,num)-dp;
dp[p]=num;
}
/*二分法找第一个大于等于num的位置
else
{
int low=0,high=cnt-1;
int pos; //记录位置
while(low<high)
{
int mid=(low+high)/2;
if(dp[mid]>=num) //因为要求第一个大于等于num的位置,所以当dp[mid]==num时,我们要向左找第一个
{
high=mid;//不能是mid-1,因为可能会比到小于num的地方去
pos=high;
}
else //往右边找第一个等于k的值
{
low=mid+1;
pos=low;
}
}
dp[pos]=num;
}*/
}
cout<<cnt<<endl;
return ;
}

 C++STL中的upper_bound()函数和lower_bound()的使用与理解

如果要自己写这连个函数,可以参考以下的代码:

upper_bound()函数返回第一个大于x的位置

int upper_bound(int l,int r,int x)
{ while(l<r){
int mid=(l+r)/;
if(a[mid]>x)r=mid;
else l=mid+;
}
return l;
}

lower_bound()函数返回第一个大于等于x的位置

int lower_bound(int l,int r,int x)
{ while(l<r){
int mid=(l+r)/;
if(a[mid]>=x)r=mid;
else l=mid+;
}
return l;
}

lower_bound()函数使用:

它的参数就是:

1.一个数组元素的地址(或者数组名来表示这个数组的首地址,用来表示这个数组的开头比较的元素的地址,不一定要是首地址,只是用于比较的“首”地址),

2.一个数组元素的地址(对应的这个数组里边任意一个元素的地址,表示这个二分里边的比较的"结尾'地址),

3.就是一个你要二分查找的那个数。

返回值:

返回值就是返回第一次出现大于等于那个要查找的数的地址,

注意两点,

第一,是地址,不是指那个要查找的数的下标,所以就注定了在这个函数的后边就要减去一个尾巴,那就是这个数组的数组名,即这个数组的首地址,只有这样才代表那个要查找的数字的下标,当然如果没有找到那个数,也是会返回的,那么返回的又会是什么呢?下面第二点。

第二点,那就是要大于等于那个数,等于好理解,大于怎么理解呢,比如说我并没有找到那个数,加入一个的数组里边就有5个数,分别是1,1,1,3,5,而我需要找的那个数就是2,怎么返回呢?小编告诉你哦,就是返回那个第一个大于2的数的地址,就是返回3的地址,那么再有一组数据就是5个数1,1,1,3,3,还是需要找寻2,那么该返回什么呢?那就是第一个3的地址。下边来段代码你理解下吧

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n=;
int a[]={,,,,,,,,,};
int main()
{
for(int i=;i<n;i++)cout<<a[i]<<" ";
cout<<endl;
while(scanf("%d",&k))
{
cout<<k<<"的第一个大于等于它的位置在"<<((lower_bound(a,a+n,k))-a)+<<endl;
}
}

upper_bound函数的用法lower_bound函数的用法相似,不过这个唯一的不同就是返回的是第一个比我要找的那个数大的数的地址,注意,这里并没有等于,也就是说如果在5个数1,1,2,2,4,里边寻找3,那么就会返回4的地址,下边代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n=;
int a[]={,,,,,,,,,};
int main()
{
for(int i=;i<n;i++)cout<<a[i]<<" ";
cout<<endl;
while(scanf("%d",&k))
{
cout<<k<<"的第一个大于它的位置在"<<((upper_bound(a,a+n,k))-a)+<<endl;
}
}

参考自博客:https://blog.csdn.net/sdz20172133/article/details/80101838

最长上升子序列LIS(51nod1134)的更多相关文章

  1. 2.16 最长递增子序列 LIS

    [本文链接] http://www.cnblogs.com/hellogiser/p/dp-of-LIS.html [分析] 思路一:设序列为A,对序列进行排序后得到B,那么A的最长递增子序列LIS就 ...

  2. 动态规划(DP),最长递增子序列(LIS)

    题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(d ...

  3. 【部分转载】:【lower_bound、upperbound讲解、二分查找、最长上升子序列(LIS)、最长下降子序列模版】

    二分 lower_bound lower_bound()在一个区间内进行二分查找,返回第一个大于等于目标值的位置(地址) upper_bound upper_bound()与lower_bound() ...

  4. 题解 最长上升子序列 LIS

    最长上升子序列 LIS Description 给出一个 1 ∼ n (n ≤ 10^5) 的排列 P 求其最长上升子序列长度 Input 第一行一个正整数n,表示序列中整数个数: 第二行是空格隔开的 ...

  5. 最长回文子序列LCS,最长递增子序列LIS及相互联系

    最长公共子序列LCS Lintcode 77. 最长公共子序列 LCS问题是求两个字符串的最长公共子序列 \[ dp[i][j] = \left\{\begin{matrix} & max(d ...

  6. 一个数组求其最长递增子序列(LIS)

    一个数组求其最长递增子序列(LIS) 例如数组{3, 1, 4, 2, 3, 9, 4, 6}的LIS是{1, 2, 3, 4, 6},长度为5,假设数组长度为N,求数组的LIS的长度, 需要一个额外 ...

  7. 1. 线性DP 300. 最长上升子序列 (LIS)

    最经典单串: 300. 最长上升子序列 (LIS) https://leetcode-cn.com/problems/longest-increasing-subsequence/submission ...

  8. 最长上升子序列(LIS)模板

    最长递增(上升)子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增(上升)子序列. 考虑两个数a[x ...

  9. hdu1025 dp(最长上升子序列LIS)

    题意:有一些穷国和一些富国分别排在两条直线上,每个穷国和一个富国之间可以建道路,但是路不能交叉,给出每个穷国和富国的联系,求最多能建多少条路 我一开始在想有点像二分图匹配orz,很快就发现,当我把穷国 ...

随机推荐

  1. 【php增删改查实例】第十八节 - login.php编写

    1.对用户名和密码进行非空判断(后台验证) $username; $password; if(isset($_POST['username']) && $_POST['username ...

  2. 一次Java内存泄漏调试的有趣经历

    人人都会犯错,但一些错误是如此的荒谬,我想不通怎么会有人犯这种错误.更没想到的是,这种事竟发生在了我们身上.当然,这种东西只有事后才能发现真相.接下来,我将讲述一系列最近在我们一个应用上犯过的这种错误 ...

  3. sublime text3 安装package control 出现问题解决过程记录

    1.安装package control 失败 通过最简单的自动安装 package control 失败(详见package control官网). 报错展示: File "./python ...

  4. ros-安装

    1.安装了ubuntu for ros. 运行评论下边那条命令: 2.rtabamp 3.准备安装机器人导航仿真系统:https://blog.csdn.net/wangchao7281/articl ...

  5. PHP利用GD库处理图片方法实现

    这里写的是完成每个功能的函数,可以复制单个函数直接使用,这里的每个函数都是另外一篇PHP常用类------图片处理类Image当中的方法进行细化,可以参考一下 废话不多说,直接付代码吧! 添加水印(文 ...

  6. TDD中测试替身学习总结

    在使用TDD开发时,经常会遇到需要被测对象需要依赖其他子系统的情况,但是你希望将测试代码跟依赖项隔离,以保证测试代码仅仅针对当前被测对象或方法展开,这时候你需要的是测试替身.测试替身可以分为四类:- ...

  7. slf4j的java包冲突问题

  8. 终端curl调用jenkins自动化持续集成

    1.curlcurl是利用URL语法在命令行方式下工作的开源文件传输工具.它被广泛应用在Unix.多种Linux发行版中,并且有DOS和Win32.Win64下的移植版本. 1.1 获取url指向的页 ...

  9. pxe+kickstart 自动化部署linux操作系统

    kickstart 是什么? 批量部署Linux服务器操作系统 运行模式: C/S client/server 服务器上要部署: DHCP tftp(非交互式文件共享) 安装系统的三个步骤: 1.加载 ...

  10. CodeGear RAD 2007 SP4 最新下载及破解

    CodeGear RAD 2007 up4最新下载及破解 官方http下载: http://altd.codegear.com/download/radstudio2007/CodeGearRADSt ...