最长递增子序列(Longest Increasing Subsequence) ,我们简记为 LIS。

:求一个一维数组arr[i]中的最长递增子序列的长度,如在序列1,-1,2,-3,4,-5,6,-7中,最长递增子序列长度为4,序列为1,2,4,6。

 解法一:快速排序+LCS

刚开始做这道题的时候,由于之前做过几道LCS的题,于是最先想到的是快速排序+LCS的方法。这种方法解决了当时只计算单个case的问题,但是后来面对计算多个    case的问题的时候,第一次遇到Memory Limit Exceeded。于是就意识到这种简单解法的时间和空间的复杂度都太高了。于是只能另寻他法。

public class Main {
static int n;
static int[] a;
static int[] b;
static int[][] z;
public static void QuickSort(int[] a){
QSort(a,1,n);
}
public static void QSort(int[] a,int p,int r){
if(p<r)
{
int q=Partition(a,p,r);
QSort(a,p,q-1);
QSort(a,q+1,r);
}
} public static int Partition(int[] a,int p,int r){
int x=a[r];
int i=p-1;
for(int j=p;j<r;j++)
{
if(a[j]<=x){
i=i+1;
swap(a, i, j);
}
}
swap(a, i+1, r);
return i+1;
}
public static void swap(int[] a, int i,int j){
int temp;
temp=a[j];
a[j]=a[i];
a[i]=temp;
}
public static int LCS(int a[],int[] b){
z=new int [n+1][n+1];
int i,j;
for( i=0;i<=n;i++)
z[i][0]=0;
for( j=0;j<=n;j++)
z[0][j]=0; for(i=1;i<=n;i++){
for( j=1;j<=n;j++){
if(a[i]==b[j]){
z[i][j]= z[i-1][j-1]+1;
}
else
z[i][j]=z[i-1][j] > z[i][j-1] ?z[i-1][j]:z[i][j-1];
}
}
return z[n][n];
}
public static void main(String[] args) {
int arr[] = {1,-1,2,-3,4,-5,6,-7};
n=arr.length;
a=new int[n+1];
b=new int[n+1];
int i,j;
for(i=1;i<=n;i++){
b[i]=a[i];
}
QuickSort(a);
//控制严格递增
for(i=1;i<n;i++){
for(j=i+1;j<=n;j++){
if(a[i]!=-1 && a[i]==a[j])
a[j]=-1;
}
}
System.out.println(LCS(a,b));
}
}

解法二:DP(O(N^2))

从后向前分析,很容易想到,第i个元素之前的最长递增子序列的长度要么是1(单独成一个序列),要么就是第i-1个元素之前的最长递增子序列加1,可以有状态方程:

  LIS[i+1] = max{1,LIS[k]+1},aray[k],for any k <=i.

即如果array[i+1]大于array[k],那么第i+1个元素可以接在LIS[k]长的子序列后面构成一个更长的子序列。于此同时array[i+1]本身至少可以构成一个长度为1的子序列。

public int LIS(int[] arr, int size){
int dp[40]; /* dp[i]记录到[0,i]数组的LIS */
int lis;    /* LIS 长度 */
 for(int i = 0; i < size; ++i){
dp[i] = 1;
for(int j = 0; j < i; ++j){
if(arr[i] > arr[j] && dp[i] < dp[j] + 1){
dp[i] = dp[j] + 1;
if(dp[i] > lis){
lis = dp[i];
}
}
}
}
return lis;
}

解法三:二分查找+DP(O(nlogn))

在解法二中,当考察第i+1个元素的时候,我们是不考虑前面i个元素的分布情况的。现在我们从另一个角度分析,即当考察第i+1个元素的时候考虑前面i个元素的情况。

      目的:我们期望在前i个元素中的所有长度为len的递增子序列中找到这样一个序列,它的最大元素比arr[i+1]小,而且长度要尽量的长,如此,我们只需记录len长度的递增子序列中最大元素的最小值就能使得将来的递增子序列尽量地长。

方法:维护一个数组MaxV[i],记录长度为i的递增子序列中最大元素的最小值,并对于数组中的每个元素考察其是哪个子序列的最大元素,二分更新MaxV数组,最终i的值便是最长递增子序列的长度。

仔细的分析请看最长递增子序列 O(NlogN)算法

public class LIS {
/* 最长递增子序列 LIS
* 设数组长度不超过 30
* DP + BinarySearch
*/
static int[] MaxV=new int[30]; /* 存储长度i+1(len)的子序列最大元素的最小值 */
static int len; /* 存储子序列的最大长度 即MaxV当前的下标*/
static int BinSearch(int[] MaxV, int size, int x){ /* 返回MaxV[i]中刚刚不小于x的那个元素的下标 */
int left = 0, right = size-1;
while(left <= right){
int mid = (left + right) / 2;
if(MaxV[mid] <= x){
left = mid + 1;
}else{
right = mid - 1;
}
}
return left;
} static int getLIS(int[] arr, int size){
MaxV[0] = arr[0]; /* 初始化 */
len = 1;
for(int i = 1; i < size; ++i){ /* 寻找arr[i]属于哪个长度LIS的最大元素 */ if(arr[i] > MaxV[len-1]){ /* 大于最大的自然无需查找,否则二分查其位置 */ MaxV[len++] = arr[i];
}else{
int pos = BinSearch(MaxV,len,arr[i]);
MaxV[pos] = arr[i];
}
}
return len;
} /**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int arr[] = {1,-1,2,-3,4,-5,6,-7};
/* 计算LIS长度 */
System.out.println(getLIS(arr,arr.length));
}
}

参考资料:

编程之美》 2.16

Felix’s Blog:最长递增子序列 O(NlogN)算法

勇幸|Thinking (http://www.ahathinking.com)

版权声明:本文为博主原创文章,未经博主允许不得转载。

最长递增子序列(LIS)的更多相关文章

  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. 最长回文子序列LCS,最长递增子序列LIS及相互联系

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

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

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

  5. 算法面试题 之 最长递增子序列 LIS

    找出最长递增序列 O(NlogN)(不一定连续!) 参考 http://www.felix021.com/blog/read.php?1587%E5%8F%AF%E6%98%AF%E8%BF%9E%E ...

  6. 算法之动态规划(最长递增子序列——LIS)

    最长递增子序列是动态规划中最经典的问题之一,我们从讨论这个问题开始,循序渐进的了解动态规划的相关知识要点. 在一个已知的序列 {a1, a 2,...an}中,取出若干数组成新的序列{ai1, ai ...

  7. 最长递增子序列 LIS 时间复杂度O(nlogn)的Java实现

    关于最长递增子序列时间复杂度O(n^2)的实现方法在博客http://blog.csdn.net/iniegang/article/details/47379873(最长递增子序列 Java实现)中已 ...

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

    最长递增子序列是动态规划中经典的问题,详细如下: 在一个已知的序列{a1,a2,...,an}中,取出若干数组组成新的序列{ai1,ai2,...,aim},其中下标i1,i2,...,im保持递增, ...

  9. 最长递增子序列LIS再谈

    DP模型: d(i) 以第 i 个元素结尾的最长递增子序列的长度. 那么就有 d(i) = max(d(j)) + 1;(j<i&&a[j]<a[i]),答案 max(d( ...

  10. POJ 1836 Alignment 最长递增子序列(LIS)的变形

    大致题意:给出一队士兵的身高,一开始不是按身高排序的.要求最少的人出列,使原序列的士兵的身高先递增后递减. 求递增和递减不难想到递增子序列,要求最少的人出列,也就是原队列的人要最多. 1 2 3 4 ...

随机推荐

  1. thinkphp将APP_DEBUG常量设置为false后报错的问题

    ThinkPHP 将 APP_DEBUG 常量设置为 false 后出现了下面的问题: Parse error: syntax error, unexpected T_STRING in \www\R ...

  2. JS表单提交

    测试一: function submit(){var form1=document.getElementById("form1")form1.action="/manag ...

  3. 几则js表达式

    过滤大段文本里的标签.标签格式 <...>,如下匹配标签然后替换成空 校验邮箱是否符合: 去掉行首行尾空格: 检测字符串是否包含中文:(utf8编码)

  4. C#读取excel 找不到可安装的ISAM

    实在没有办法了 就仔细的查看了 一下数据链接字符串: string strConn = "Provider=Microsoft.Jet.Oledb.4.0;Data Source=" ...

  5. 图片加载控件glide

    基本用法 //(1)加载网络图片 tvGlide1.setText("(1)加载网络图片"); Glide.with(this).load("http://img1.im ...

  6. 装箱问题【STL】

    7-9 装箱问题(20 分) 假设有N项物品,大小分别为s​1​​.s​2​​.-.s​i​​.-.s​N​​,其中s​i​​为满足1≤s​i​​≤100的整数.要把这些物品装入到容量为100的一批箱 ...

  7. 顺序表的静态存储(C语言实现)

    顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构.  1.顺序表的结构体声明 #define MAX_SIZE 5 //定义数组的大小 typed ...

  8. Hadoop 2.x 之 HA 简介

    HA结构图 HA是用来解决单点故障问题 DN: DataNode,启动时会往所有的NameNode汇报 NN: NameNode(主 Active(一个)   备 Standby(可以有多个)) Jo ...

  9. jQuery cookie 实现记住用户名和密码功能

    jQuery cookie 实现记住用户名和密码功能 HTML代码 <div class="wrap"> <div class="line-top&qu ...

  10. TYOI Day1 travel:Tree dp【处理重复走边】

    题意: 给你一棵树,n个节点,每条边有长度. 然后有q组询问(u,k),每次问你:从节点u出发,走到某个节点的距离mod k的最大值. 题解: 对于无根树上的dp,一般都是先转成以1为根的有根树,然后 ...