Lintcode--010(最长上升子序列)
给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。LIS(longestIncreasingSubsequence)
说明:
最长上升子序列的定义:
最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。
最长上升子序列问题,也就是Longest increasing subsequence
,缩写为LIS
。是指在一个序列中求长度最长的一个上升子序列的问题,是动态规划中一个相当经典问题。在这里我们可以看到,这个上升实质上就是一个对<
进行定义的过程,所以我们求解的其实是一类问题,也就是在给定序列中求解长度最长的符合某一性质的子序列的问题。
给出 [5,4,1,2,3]
,LIS 是 [1,2,3]
,返回 3
给出 [4,2,4,5,3,7]
,LIS 是 [2,4,5,7]
,返回 4
要求时间复杂度为O(n^2) 或者 O(nlogn)
dp[i]
表示以i结尾的子序列中LIS的长度。然后我用dp[j](0<=j<i)
来表示在i之前的LIS的长度。然后我们可以看到,只有当a[i]>a[j]
的时候,我们需要进行判断,是否将a[i]加入到dp[j]当中。为了保证我们每次加入都是得到一个最优的LIS,有两点需要注意:第一,每一次,a[i]都应当加入最大的那个dp[j],保证局部性质最优,也就是我们需要找到max(dp[j](0<=j<i))
;第二,每一次加入之后,我们都应当更新dp[j]的值,显然,dp[i]=dp[j]+1
。如果写成递推公式,我们可以得到
dp[i]=max(dp[j](0<=j<i))+(a[i]>a[j]?1:0)
。于是我们就能够得到O(n^2)的动态规划方法。
class Solution {
public:
/**
* @param nums: The integer array
* @return: The length of LIS (longest increasing subsequence)
*/
int longestIncreasingSubsequence(vector<int> nums) {
// write your code here int n=nums.size();
int dp[n];
memset(dp,,sizeof(dp)); int Max;
for(int i=;i<n;i++){//每次求以第i个数为终点的最长上升子序列的长度
Max=;
for(int j=;j<i;j++){//查看以第j个数为终点的最长上升子序列
if(nums[i]>nums[j]){
Max=max(Max,dp[j]);
}
}
dp[i]=Max+;
}
Max=;
for(int i=;i<n;i++){
if(dp[i]>Max){
Max=dp[i];
}
}
return Max;
}
};
上面的方法,我们花费了很多时间在寻找最大的dp[j]上。如果有办法让这个dp[j]变成一个递增的序列,我们就能使用二分来进行优化,从而使得复杂度下降为O(nlogn)
了。
方法4:动态规划+二分法(效率最高)
假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。
下面一步一步试着找出它。
我们定义一个序列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)~!
//在非递减序列 arr[s..e](闭区间)上二分查找第一个大于等于key的位置,如果都小于key,就返回e+1
int upper_bound(int arr[], int s, int e, int key)
{
int mid;
if (arr[e] <= key)
return e + 1;
while (s < e)
{
mid = s + (e - s) / 2;
if (arr[mid] <= key)
s = mid + 1;
else
e = mid;
}
return s;
} int LIS(int d[], int n)
{
int i = 0, len = 1, *end = (int *)alloca(sizeof(int) * (n + 1));
end[1] = d[0]; //初始化:长度为1的LIS末尾为d[0]
for (i = 1; i < n; i++)
{
int pos = upper_bound(end, 1, len, d[i]); //找到插入位置
end[pos] = d[i];
if (len < pos) //按需要更新LIS长度
len = pos;
}
return len;
}
class Solution {
public:
/**
* @param nums: The integer array
* @return: The length of LIS (longest increasing subsequence)
*/
int longestIncreasingSubsequence(vector<int> nums) {
// write your code here int n=nums.size();
int dp[n];
if(n==){
return ;
}
memset(dp,,sizeof(int)*n);
int len=;
dp[]=nums[]; for(int i=;i<n;i++){
int pos=lower_bound(dp,dp+len,nums[i])-dp;
dp[pos]=nums[i];
len=max(len,pos+);
}
return len;
}
};
在第二种方法中,我们花费了很多时间在寻找最大的dp[j]上。如果有办法让这个dp[j]变成一个递增的序列,我们就能使用二分来进行优化,从而使得复杂度下降为O(nlogn)
了。
幸
运的是,这种方法确实是存在的。我们可以使用dp[i]来保存在前i个数中最大的那个数,很容易可以理解,这个dp[i]已经是单调不减的。接下来的处理
其实有些贪心的思想,对于每一个a[i],我们都在dp数组中寻找比它大的第一个数的下标,不妨设为pos,然后用a[i]来更新dp[pos]。于是我
们可以明白,len就应当是max(len, pos+1)
。
在这里我们使用lower_bound函数,这个函数将会返回小于等于val的第一个值的指针,如果不存在就返回end指针。
Lintcode--010(最长上升子序列)的更多相关文章
- LintCode 77: 最长公共子序列
public class Solution { /** * @param A, B: Two string. * @return: the length of the longest common s ...
- 最长回文子序列LCS,最长递增子序列LIS及相互联系
最长公共子序列LCS Lintcode 77. 最长公共子序列 LCS问题是求两个字符串的最长公共子序列 \[ dp[i][j] = \left\{\begin{matrix} & max(d ...
- C++版 - Lintcode 77-Longest Common Subsequence最长公共子序列(LCS) - 题解
版权声明:本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C++版 - L ...
- lintcode:最长公共子序列
题目 最长公共子序列 给出两个字符串,找到最长公共子序列(LCS),返回LCS的长度. 样例 给出"ABCD" 和 "EDCA",这个LCS是 "A& ...
- lintcode:最长上升子序列
题目 最长上升子序列 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度. 样例 给出[5,4,1,2,3],这个LIS是[1,2,3],返回 3 给出[4,2,4,5,3,7],这个L ...
- lintcode 77.Longest Common Subsequence(最长公共子序列)、79. Longest Common Substring(最长公共子串)
Longest Common Subsequence最长公共子序列: 每个dp位置表示的是第i.j个字母的最长公共子序列 class Solution { public: int findLength ...
- HDU-4521 小明系列问题——小明序列 间隔限制最长上升子序列
题意:给定一个长度为N的序列,现在要求给出一个最长的序列满足序列中的元素严格上升并且相邻两个数字的下标间隔要严格大于d. 分析: 1.线段树 由于给定的元素的取值范围为0-10^5,因此维护一棵线段树 ...
- lintcode-76-最长上升子序列
76-最长上升子序列 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度. 说明 最长上升子序列的定义: 最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列 ...
- 用python实现最长公共子序列算法(找到所有最长公共子串)
软件安全的一个小实验,正好复习一下LCS的写法. 实现LCS的算法和算法导论上的方式基本一致,都是先建好两个表,一个存储在(i,j)处当前最长公共子序列长度,另一个存储在(i,j)处的回溯方向. 相对 ...
随机推荐
- Delphi IDHTTP用法详解(六种用法)
一.IDHTTP的基本用法 IDHttp和WebBrowser一样,都可以实现抓取远端网页的功能,但是http方式更快.更节约资源,缺点是需要手动维护cook,连接等 IDHttp的创建,需要引入ID ...
- Building Web Apps with SignalR, Part 1
Building Web Apps with SignalR, Part 1 In the first installment of app-building with SignalR, learn ...
- vector,list和deque区别
stl提供了三个最基本的容器:vector,list,deque. vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由 ...
- 【转】Android:控件Spinner实现下拉列表
原文网址:http://www.cnblogs.com/tinyphp/p/3858920.html 在Web开发中,HTML提供了下拉列表的实现,就是使用<select>元素实现一个下拉 ...
- DLL模块:C++在VS下创建、调用dll
1.dll的优点 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用.比较常见的例子是各种应用程序框架,ATL.MFC等 ...
- Codeforces Round #277.5 (Div. 2) --E. Hiking (01分数规划)
http://codeforces.com/contest/489/problem/E E. Hiking time limit per test 1 second memory limit per ...
- 【KMP】Cyclic Nacklace
KMP算法 next[]深入了解,做到这题才真正明白next[]的用法,希望后面的题目能理解的更深刻. Problem Description CC always becomes very depre ...
- SqlServer 自动备份、自动删除7天前备份
-----sqlserver 数据异地备份 默认删除 七天前的数据 -----该代码可配置成SQLServer作业里做调度,或者配置成任务计划进行执行 ----挂载异地盘符 exec master.. ...
- iBatis查询结果部分为null的解决办法
今天第一天接触iBatis,没有系统学习过,遇到了一个简单却闹心的错误:用iBatis查询数据库中某个表的多列结果作为一个对象返回时,会出现对象的部分属性为null值得错误.例如,查询用户表中的用户I ...
- Servlet问题:servlet cannot be resolved to a type解决办法
工程里的路径权限高,并且eclipse并到classpath里寻找jar位置,所以我就到我的java项目里 项目名-->右键 Property-->选择 Java Build Path-- ...