问题:给定一组数 a0,a0,....,an-1求该序列的最长递增(递减)序列的长度。

最长递增子序列长度的求法有O(n^2)和O(nlogn)两种算法.

1.复杂度为O(n^2)的算法。

设L[i]表示以a[i]结尾的最长递增子序列的长度。则ans=max{L[1],...,L[n]};当i=1时,显然长度为1,即L[1]=1;L[i]的递归方程如下:

L[i]=max{L[j]}+1  (j<i且a[j]<a[i]).

于是可以采用自底向上来计算L[2]...L[n].

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100001;
int L[N],a[N];
int ans,n;
void LIS(){
L[0] = 1, ans = 1;
int i,j;
for (i = 1; i < n; i++){
L[i] = 1;
for (j = 0; j < i; j++){
if (a[j] < a[i] && L[i] < L[j] + 1)
L[i] = L[j] + 1;
}
ans = max(ans, L[i]);
}
}
int main(){
scanf("%d", &n);
int i;
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
ans = 0;
LIS();
//输出以a[i]结尾的最大递增序列长度
for (i = 0; i < n; i++)
printf("%d\n", L[i]);
//输出整个序列的最大递增序列长度
printf("%d\n", ans);
return 0;
}

 2.时间复杂度为O(nlogn)的算法。

考虑上述动态规划计算L[k]的情况。假设有as,a满足如下条件:

1)s<t<k

2) as<at<ak

3)L[s]=L[t]

此时将ak加在as或者at的尾部得到的长度相同。但是加在谁后面会更好?显然ak加在as后面会更好。可以这样考虑,如果存在ax满足as<ax<at.这时候将将ak加在as后面显然得到的递增序列会更长。

定义数组d[i]:L[k]=i+1的所有ak的最小值。即为:d[i]={ak|L[k]=i+1};通俗的讲就是表示最大递增子序列长度为i+1,该序列的最后一个数的最小值。

d[i]有如下性质:

1)d[i]的值在整个计算过程中是单调非递增的

2) 序列d[0]..d[n-1]是严格递增的。d[0]<d[1]<...<d[n-1].

由于性质2),所以可以使用二分查找的办法,将总的复杂度优化为O(nlogn).算法步骤如下:

(1)初始化ans=1,d[0]=a[0],i=0

(2)若i>=n,则算法结束,否则转(3)

(3)  if   a[i]>d[ans-1]

then  d[ans]=a[i];ans=ans+1;

else

ind=Bsearch(d,ans,a[i]),d[ind]=a[i];

(4)i=i+1,转(2)

在具体实现时,函数Bserach是二分查找函数,功能是找到a[i]插入的位置。函数返回第一个大于或等于a[i]的值的下标,也即是a[i]要插入的地方。具体做时,可以使用C++库函数lower_bound(),该函数包含在头文件#include<algorithm>中,函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置。需要注意的是,他作用的序列必须是有序的。例如一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标

pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。

pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。

pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<map>
#include<queue>
#include<vector>
using namespace std;
const int N = 1000001;
int d[N], a[N];
int f[N];//f[i]用来记录以a[i]结尾的最长递增序列长度
int ans,n; //ans记录整个数组的最大递增序列的长度
void LIS(){
d[0] = a[0], ans = 1,f[0] = 1;
for (int i = 1; i <n; i++){
int pos =lower_bound(d, d + ans, a[i])-d; //计算a[i]插入的下标
d[pos] = a[i];
if (pos == ans)
ans++;
f[i] = pos + 1; //a[i]插入的下标就是pos处,所以a[i]前面必有pos个数的递增子序列
}
}
int main(){
int T;
scanf("%d", &T);
while (T--){
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
ans = 0;
LIS();
for (int i = 0; i < n; i++)
cout << f[i] << (i == n - 1 ? "\n":" ");
}
return 0;
}

  

最长递增子序列问题—LIS的更多相关文章

  1. 最长递增子序列(LIS)(转)

    最长递增子序列(LIS)   本博文转自作者:Yx.Ac   文章来源:勇幸|Thinking (http://www.ahathinking.com)   --- 最长递增子序列又叫做最长上升子序列 ...

  2. [51Nod 1218] 最长递增子序列 V2 (LIS)

    传送门 Description 数组A包含N个整数.设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可 ...

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

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

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

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

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

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

  6. 最长递增子序列(LIS)

    最长递增子序列(Longest Increasing Subsequence) ,我们简记为 LIS. 题:求一个一维数组arr[i]中的最长递增子序列的长度,如在序列1,-1,2,-3,4,-5,6 ...

  7. 求解最长递增子序列(LIS) | 动态规划(DP)+ 二分法

    1.题目描述     给定数组arr,返回arr的最长递增子序列. 2.举例     arr={2,1,5,3,6,4,8,9,7},返回的最长递增子序列为{1,3,4,8,9}. 3.解答      ...

  8. 动态规划----最长递增子序列问题(LIS)

    题目: 输出最长递增子序列的长度,如输入 4 2 3 1 5 6,输出 4 (因为 2 3 5 6组成了最长递增子序列). 暴力破解法:这种方法很简单,两层for循环搞定,时间复杂度是O(N2). 动 ...

  9. Python动态规划求解最长递增子序列(LIS)

    原始代码错误,移步博客查看O(N^2)及优化的O(N*logN)的实现:每天一道编程题--最长递增子序列

随机推荐

  1. python小脚本(18-11.10)-修改excle后批量生成,作用:导入数据时,系统做了不能导入重复数据时的限制时使用 -本来是小白,大神勿扰

    from testcase.test_mokuai.operation_excle import OperationExcleimport shutil class test_daoru(): #一个 ...

  2. Python的网络编程socket模块

    (1)利用socket进行简单的链接 Python里面的socket支持UDP.TCP.以及进程间的通信,socket可以把我们想要发送的东西封装起来,发送过去,然后反解成原来的样子,事实上网路通信可 ...

  3. 【bzoj4012】[HNOI2015]开店 动态点分治+STL-vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的想法当然非常好啦,但是她们也发现她们面临着一个问题 ...

  4. linux命令 显示文件内容

    通过命令+文件名查看内容.如下命令可以查看.1, cat :由第一行开始显示文件内容:2,tac:从最后一行开始显示,可以看出tac与cat字母顺序相反:3,nl:显示的时候输出行号:4,more:一 ...

  5. Python之面向对象:面向对象基础

    一.面向过程.面向对象对比 1.面向过程 根据业务逻辑从上到下写垒代码 2.函数式思想 将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 3.面向对象 对函数进行分类和封装 1.2.3一步 ...

  6. 解决jsp在ios小屏手机下面滑动不流畅的问题

    今天做好的静态文件发给后台改成jsp之后,发现原本流畅滑动的页面在iphone5下面变得一卡一卡的. 之后加上了 -webkit-overflow-scrolling: touch; 这个属性之后,成 ...

  7. github的一些简单用法

     关于 项目 上传  大多数人都是使用命令行上传 步骤分为以下几步: 在github上创建你的  repositories  ->github.com - >右下角  new reopsi ...

  8. DataSet导出到Excel文件

    public static void ExportToExcel(DataSet source, string fileName) { System.IO.StreamWriter excelDoc ...

  9. 10个HTML和CSS必须知道的重点难点问题

    前端日刊 登录 10个HTML和CSS必须知道的重点难点问题 2018-02-26 阅读 2982 收藏 6 原链:segmentfault.com 分享到:   前端必备图书<深入浅出Node ...

  10. java中的序列化与反序列化,还包括将多个对象序列化到一个文件中

    package Serialize; /** * Created by hu on 2015/11/7. */ //实现序列化必须实现的接口,这就是一个空接口,起到标识的作用 import java. ...