题目链接:点击这里
从序列单调性的角度进行优化,LIS的O(nlogn)说白了其实是贪心算法。
以 为例,求其最长上升子序列。
策略:应该尽可能地让上升子序列的元素整体变小,以便可以加入更多的元素。
开辟一个新的数组 :
-
第一个数是 ,先加进去,那么为 ;
-
再来看下一个数 ,它比 小,如果它与 替换是不是可以让目前子序列变得更小,更方便以后元素的加入呢?是的,所以现在为 ;
-
再来看他的下一个元素 ,它比 大,所以加在后面,即 ;
-
再看下一个元素是 ,它比 要小,所以为了保证子序列整体尽可能的小,以便可以加入更多的元素,从目前的序列中查找出第一个比它大的数替换掉,那么就变成了 ;
-
下一个数是 ,那么序列变为 ;
-
再下一个数为 ,那么序列为 ;
-
再下一个数为 ,那么序列为
最终序列里有 个元素,所以最长子序列的个数为 ,但是这个序列是一个伪序列,里面的元素并不是真正的最长上升子序列,而仅仅和最长上升子序列的个数一样。
以 为例,最终序列为 不是LIS,但LIS的长度确实为 。
lower_bound( begin, end, num );
功能:从数组的 begin 位置到 end-1 位置二分查找第一个大于或等于 num 的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 100010;
int a[N], dp[N];
int main()
{
int n;
cin>>n;
for(int i = 1; i <= n; ++i) cin>>a[i];
int len = 1;
dp[1] = a[1];
for(int i = 2; i <= n; ++i)
{
if(a[i] > dp[len])
dp[++len] = a[i];
else
*lower_bound(dp + 1, dp + len + 1, a[i]) = a[i];
}
cout<<len<<endl;
return 0;
}