Longest Increasing Subsequence
For a given sequence A = {a0, a1, … , an-1}, find the length of the longest increasing subsequnece (LIS) in A.
An increasing subsequence of A is defined by a subsequence {ai0, ai1, … , aik} where 0 ≤ i0 < i1 < … < ik < n and ai0 < ai1 < … < aik.
Input
n
a0
a1
:
an-1
In the first line, an integer n is given. In the next n lines, elements of A are given.
Output
The length of the longest increasing subsequence of A.
Constraints
1 ≤ n ≤ 100000
0 ≤ ai ≤ 109
Sample Input 1
5
5
1
3
2
4
Sample Output 1
3
Sample Input 2
3
1
1
1
Sample Output 2
1
解题思路:
一道裸的最长上升子序列问题,动态转移方程为
f[i]=max(f[i],f[j]+1)
不过由于数据范围达到了100000,O(n^2)的算法已经无法满足,因此需要用到O(nlogn)的算法。
其中,阶段和n^2的算法一样,也是定义一个f[i]表示前i个数的最长上升子序列长度,下面代码中用的是d[i]。在n^2的算法中,我们的内层循环是循环前i个数,目的是为了找到数值上比a[i]小的数中最长上升子序列数最大的那个数来更新f[i]。现在可以定义一个s[i]表示最长上升子序列长度为i的序列末尾a[i]的最小值。
举个例子:
a[] = {5,1,3,2,4}
s[] = {1,2,4}
d[] = {1,1,2,2,3}
其中s[i]的下标就是它所对应的最长上升子序列的长度,里面的值也就是我们需要查找的值,在里面找到一个比a[i]小的最大的值,他的下标就是我们的f[i]的值,并且具有单调性,所以可以用二分查找来优化,这样就可以将内层循环优化到logn的时间复杂度。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100000 + 5;
int n;
int a[maxn],s[maxn],d[maxn];
int Binary_search(int l,int r,int v)
{
while(l <= r)
{
int mid = l + (r-l)/2;
if(s[mid] < v)l = mid + 1;
else r = mid-1;
}
return l;
}
int dp()
{
int ans = 0;
for(int i = 1;i <= n; i++)s[i] = 1000000000;
for(int i = 1;i <= n; i++)
{
int pos = Binary_search(1,i,a[i]);
d[i] = pos;
s[d[i]] = min(s[d[i]],a[i]);
ans = max(ans,d[i]);
}
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n; i++)scanf("%d",&a[i]);
int ans = dp();
printf("%d\n",ans);
}