最长单增子序列问题

问题描述:有一个长为n的数列a0,a1,a2........a(n-1)。请求出这个序列中最长的单增子序列的长度。单增子序列的定义是:对于任意的 i<j,都满足ai<aj。

这个问题就是著名的最长单增子序列(LIS)问题。对于这道问题,我们可以利用动态规划来进行求解:假设dp[i]表示以a[i]为末尾的最长单增子序列的长度,则在得到dp[i]时,我们可以这样做:初始化dp[i]为1,利用一个j变量遍历已经访问过的数组a中的值,如果此时a[i]>a[j],表示我们可以在原来的子序列之后加上一个构成一个新的以ai结尾的单增子序列,这时,如果dp[i]的值小于dp[j]+1的值时,我们就将其更新。这样我们就可以得到:时间复杂度为O(n^2)。

dp[i] = max{dp[j] + 1, 1} if j<i and a[j] < a[i]。

#include<iostream>
#define max(a, b) ((a)>(b)?(a):(b))

const int INF = 1000000;
const int n = 6;
int a[n] = {4, 2, 3, 1, 5, 5};

int dp[n];

/**
dp[i]表示以a[i]为末尾的最长单增子序列的长度
*/
int dp1(){
	int res = 0;
	for (int i = 0; i < n; i++){
		dp[i] = 1;
		for (int j = 0; j < i; j++)
		{
			if (a[i] > a[j])
			{
				dp[i] = max(dp[i], dp[j]+1);
			}
		}
		res = max(dp[i], res);
	}

	return res;
}

int main(){
	printf("%d\n", dp1());
	system("pause");
	return 0;
}

接下来我们换一种思路来想,如果子序列的长度相同的话,那么取得的末尾值越小就越有优势。基于这种思路我们可以利用这样的假设:dp[i]表示长度为i+1的上升子序列中末尾元素的最小值。首先我们对dp数组用无穷大INF进行初始化,对于每一个ai,如果j==0,或者dp[j-1]<a[i],我们就用dp[j] = min(dp[j], a[i])来进行更新。这样我们仍然需要一个两层的循环,但是可以进行优化,对于内层的循环:由于dp数组是单增的(可以用反证法证明,如果i<j,而dp[i]
> dp[j],则在以dp[j]结尾的单增子序列中的第i位一定小于dp[i],这样就违背了当初对dp数组的假设),我们可以使用一个二分搜索来找的我们需要更新的位置,即在dp数组中找到大于或等于a[i]且最近的位置,这样就将时间复杂度降低为O(n*logn)。

#include<iostream>
#define max(a, b) ((a)>(b)?(a):(b))

const int INF = 1000000;
const int n = 6;
int a[n] = {4, 2, 3, 1, 5, 5};

int dp[n];

/*
返回dp数组中找到>=target且最近的位置
*/
int binary_search(int target){
	int l = -1, r = n, m;

	while(l+1 != r){
		m = (l+r)/2;
		if (dp[m] < target)
			l = m;
		else
			r = m;
	}
	return r;
}

/**
dp[i]表示长度为i+1的上升子序列中末尾元素的最小值
*/
int dp2(){

	for (int i = 0; i < n; i++)
	{
		dp[i] = INF;
	}
	for (int i = 0; i < n; i++)
	{
		int p = binary_search(a[i]);
		dp[p] = a[i];
	}

	return binary_search(INF);
}

int main(){
	printf("%d\n", dp2());
	system("pause");
	return 0;
}
时间: 2024-10-10 01:24:35

最长单增子序列问题的相关文章

动规讲解基础讲解七——最长单增子序列

(LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的. 例如给定序列{1,6,3,5,4},答案是3,因为{1,3,4}和{1,3,5}就是长度最长的两个单增子序列. 处看此题,怎么做? 万能的枚举?枚举全部2^n个子序列,找出最长的,固然可以,就是复杂度太高.我们为什么要枚举呢?因为要知道取了哪些数,其实我们只需要考虑上一个数和取了几个数就可以了吧?因为单增的意思

动态规划5-最长单增子序列

(LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的. 例如给定序列{1,6,3,5,4},答案是3,因为{1,3,4}和{1,5,4}就是长度最长的两个单增子序列. 处看此题,怎么做? 万能的枚举?枚举全部2^n个子序列,找出最长的,固然可以,就是复杂度太高.我们为什么要枚举呢?因为要知道取了哪些数,其实我们只需要考虑上一个数和取了几个数就可以了吧?因为单增的意思

poj1836——dp,最长上升子序列(lis)

poj1836——dp,最长上升子序列(lis) Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13767   Accepted: 4450 Description In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight

POJ 2533 - Longest Ordered Subsequence - [最长递增子序列长度][LIS问题]

题目链接:http://poj.org/problem?id=2533 Time Limit: 2000MS Memory Limit: 65536K Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ...,

14-高效求最长公共子序列(二维数组存不下)

/*                                   See LCS again时间限制:1000 ms  |  内存限制:65535 KB难度:3 描述 There are A, B two sequences, the number of elements in the sequence is n.m; Each element in the sequence are different and less than 100000. Calculate the length

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

最长公共子序列的代码实现

关于最长公共子序列(LCS)的相关知识,http://blog.csdn.net/liufeng_king/article/details/8500084 这篇文章讲的比较好,在此暂时不再详说. 以下是我代码实现两种方式:递归+递推: 1 #include <bits/stdc++.h> 2 using namespace std; 3 int A[100]; 4 int B[100]; 5 6 //int B[]={2,3,5,6,9,8,4}; 7 int d[100][100]={0};

HDU 3998 Sequence (最长递增子序列+最大流SAP,拆点法)经典

Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1666    Accepted Submission(s): 614 Problem Description There is a sequence X (i.e. x[1], x[2], ..., x[n]). We define increasing subsequ

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

找出最长递增序列 O(NlogN)(不一定连续!) 参考 http://www.felix021.com/blog/read.php?1587%E5%8F%AF%E6%98%AF%E8%BF%9E%E6%95%B0%E7%BB%84%E9%83%BD%E6%B2%A1%E7%BB%99%E5%87%BA%E6%9D%A5 我就是理解了一下他的分析 用更通俗易懂的话来说说题目是这样 d[1..9] = 2 1 5 3 6 4 8 9 7 要求找到最长的递增子序列首先用一个数组b[] 依次的将d里面