动态规划之最长上升子序列
问题描述
输入数据
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出
序列中的N个整数,这些整数的取值范围都在0到10000。
输出要求
最长上升子序列的长度。
输入样例
7
1 7 3 5 9 4 8
输出样例
4
输入样例
9
2 7 1 5 6 4 3 8 9
输出样例
5
解法一:递归式
思路:假设有n个数存储在一维数组a里(下标从0开始)。
那么前n个数中的最长上升子序列的长度等于从a[0]开始到a[n-2]为止前n-1个数中小于a[n]的所有数的最长上升子序列长度的最大值加1。
这话有些绕,下面画图说明:
值得注意的是,当m=1或者n=1时,即a[n-1]的前n-1个元素都比a[n-1]大或者数组a中只有a[0]这一个元素时,最大上升子序列的长度应为1。
#include<iostream>
#define MAX 1000
#define INF 999999999
using namespace std;
int a[MAX];
int cache[MAX];
/* Longest increasing subsequence
*/
int LIS(int n)
{
int i = 0;
int lon = 0;
int re = 0;
int sign = 0;
if(n == 0)
return 1; //数组a中只有a[0]这一个元素时,最大上升子序列的长度应为1
for(i = n-1; i>=0; i--)
{
if(a[i]<=a[n])
{
sign = 1;
re = LIS(i);
if(lon < re)
{
lon = re;
}
}
}
if(sign == 0) return 1; //a[n-1]的前n-1个元素都比a[n-1]大时,最大上升子序列的长度应为1
return lon + 1;
}
int main()
{
int n = 0;
int i = 0;
cin >> n;
for(i = 0; i<n; i++)
{
cin >> a[i];
}
cout << LIS(n-1)<<endl;
return 0;
}
解法二:递推式
对于每一个元素a[i]而言,len[i] = len(z) + 1 , j<=i且a[j] <=a[i],而z是所有满足条件的j中len(j)最大的那一个。若不存在满足上述条件的j,则len(i) = 1。
#include<iostream>
#define MAX 10005
using namespace std;
int a[MAX];
int b[MAX];
// longest increasing subsequence
void LIS(int n)
{
int i= 0;
int j = 0;
int sign = 0;
for(i = 0; i<n; i++)
{
sign = 0;
for(j = i-1; j>=0; j--)
{
if(a[j]<a[i] && b[j]>=b[i])
{
b[i] = b[j];
sign = 1;
}
}
if(sign == 1)
b[i]++;
}
}
int main()
{
int n = 0;
int result = 0;
cin >> n;
for(int i = 0; i<n; i++)
{
cin >> a[i];
b[i] = 1;
}
LIS(n);
for(int i = 0; i<n; i++)
{
if(result<b[i]) result = b[i];
}
cout << result <<endl;
return 0;
}
解法三:贪心加二分
此解法的思想是用一个一维数组来存储寻找子序列过程中值的变化情况:a中的当前的元素若大于或等于一维数组的最后一个有效元素,那么就把它加入到一维数组中作为数组的最后一个元素。如果小于,那么就在一维数组中找到第一个比它大的元素并替换掉。这样最后一维数组中的所有有效元素就构成了一个最长上升子序列。
另外,在找一维数组中第一个比a中当前元素大的元素时,可以采用二分查找的形式。要注意这里的二分查找不是普通的二分查找,而是要找上确界。
综合考虑,此解法的时间复杂度为O(n*log n)。
#include<iostream>
#define MAX 10000
using namespace std;
int a[MAX];
int b[MAX];
int binary_search(int n,int a)
{
int head = 1;
int tail = n;
int middle = 0;
while(head < tail)
{
middle = tail - (tail - head)/2;
if(b[middle-1] <= a && b[middle] >=a) return middle;
if(b[middle] < a)
{
head = middle;
}
else if(b[middle-1] > a )
{
tail = middle - 1;
}
}
return 1;
}
int LIS(int n)
{
int i = 0;
int max_len = 0;
int search_result = 0;
for(i = 0; i<n; i++)
{
if(b[max_len] <= a[i])
{
b[++max_len] = a[i];
}
else
{
b[binary_search(max_len, a[i])] = a[i];
}
}
return max_len;
}
int main()
{
int n = 0;
int i = 0;
cin >> n;
for(i = 0; i<n; i++)
{
cin >> a[i];
}
cout << "The longest increasing subsequence\'s length is: ";
cout <<LIS(n)<<endl;
return 0;
}