Longest Increasing Subsequence(LIS) 一个美丽的名字
非常经典的线性结构dp
[朴素]:O(n^2)
d(i)=max{0,d(j) :j<i&&a[j]<a[i]}+1
直接两个for
[二分查找优化]:O(nlogn)
g(i):d值为i的最小的a 每次更新然后lower_bound即可 [大于等于]
lower_bound
Return iterator to lower bound
Returns an iterator pointing to the first element in the range [first,last) which does not compare less than val.
The elements are compared using operator< for the first version, and comp for the second. The elements in the range shall already be sorted according to this same criterion (operator< or comp), or at least partitioned with respect to val.
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的
没有返回最后的下一个
g单调递增,所以可以二分
[线段树优化]:同上-----实质:二维偏序问题
-------------------------------------------------------------------------------------
[例题]比如 NOIP2004合唱队形
题目描述
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
----------------------------------------------
正着一遍LIS,反着一遍LIS
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const int N=105,INF=1e6; 7 inline int read(){ 8 int x=0,f=1; 9 char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 11 while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} 12 return x; 13 } 14 15 int n,d[N],g[N],a[N],f[N],ans=0; 16 17 void dp(){ 18 for(int i=1;i<=n;i++) g[i]=INF; 19 for(int i=1;i<=n;i++){ 20 int k=lower_bound(g+1,g+1+n,a[i])-g;//printf("%d d ",k); 21 d[i]=k; 22 g[k]=a[i]; 23 } 24 reverse(a+1,a+n+1); 25 for(int i=1;i<=n;i++) g[i]=INF; 26 for(int i=1;i<=n;i++){ 27 int k=lower_bound(g+1,g+1+n,a[i])-g;//printf("%d f ",k); 28 f[i]=k; 29 g[k]=a[i]; 30 } 31 32 33 } 34 int main() { 35 n=read(); 36 for(int i=1;i<=n;i++) a[i]=read(); 37 38 dp(); 39 for(int i=1;i<=n;i++) ans=max(ans,f[n-i+1]+d[i]-1); 40 printf("%d",n-ans); 41 return 0; 42 }
----------------------------------------------
Longest Decreasing Subsequence(LDS) (不知道有没有这个名字)
也可以用二分,改一改,保留a最大的,找第一个小于等于的
1 bool cmp(int a,int b){ 2 return a>b; 3 } 4 void lds(){ 5 for(int i=1;i<=n;i++) g[i]=-INF; 6 for(int i=1;i<=n;i++){ 7 int k=lower_bound(g+1,g+1+n,a[i],cmp)-g; 8 f[i]=k; 9 g[k]=a[i]; 10 } 11 }
-----------------------------------------------
不上升,不下降
用upper_bound,找第一个大于的
[例题]
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出格式
输入格式:
一行,若干个正整数。
输出格式:
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入输出样例
389 207 155 300 299 170 158 65
6 2
-------------------------------------------------------------------------------------------------------------------------------------------
一遍最长不上升,一遍最长上升
(数据规模很小,但是练习一下nlogn)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int N=55,INF=1e7; 6 int n=0,a[N],g[N],d[N],f[N]; 7 bool cmp(int a,int b){ 8 return a>b; 9 } 10 void lds(){ 11 for(int i=1;i<=n;i++) g[i]=-INF; 12 13 for(int i=1;i<=n;i++){ 14 int k=upper_bound(g+1,g+1+n,a[i],cmp)-g; 15 f[i]=k; 16 g[k]=a[i]; 17 } 18 } 19 void lis(){ 20 for(int i=1;i<=n;i++) g[i]=INF; 21 22 for(int i=1;i<=n;i++){ 23 int k=lower_bound(g,g+1+n,a[i])-g; 24 d[i]=k; 25 g[k]=a[i]; 26 } 27 } 28 29 int main(){ 30 while(cin>>a[++n]);n--; 31 32 lds();lis(); 33 int _max=-INF; 34 for(int i=1;i<=n;i++) _max=max(_max,f[i]); 35 printf("%d ",_max); 36 37 _max=-INF; 38 for(int i=1;i<=n;i++) _max=max(_max,d[i]); 39 cout<<_max; 40 41 }