【noi 2.6_1759】LIS 最长上升子序列(DP,3种解法)

题意我就不写了。解法有3种:

1.O(n^2)。2重循环枚举 i 和 j,f[i]表示前 i 位必选 a[i] 的最长上升子序列长度,枚举a[j]为当前 LIS 中的前一个数。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int N=1010;
 8 int a[N],f[N];
 9 
10 int mmax(int x,int y) {return x>y?x:y;}
11 int main()
12 {
13     int n,ans=0;
14     scanf("%d",&n);
15     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
16     for (int i=1;i<=n;i++)
17     {
18       f[i]=1;
19       for (int j=1;j<i;j++)
20         if (a[i]>a[j]) f[i]=mmax(f[i],f[j]+1);
21       ans=mmax(ans,f[i]);
22     }
23     printf("%d",ans);
24     return 0;
25 }
View Code 1

2.O(n log n)。继正确但不高效的解法后,我们想要对时间复杂度降维。最常见的做法就是二分查找,这题就是把解法1的 j 的O(n)枚举变为O(log n)的二分。那么二分的范围肯定要包含当前的 LIS 的数,而且要知道这些数对应的 f[ ]值。因此,我们只能保存扫完前 i 个选出的最优的 LIS,上述2个条件都可以满足。同时不断扩大和更新(存尽量小的数)这个序列。

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 const int N=1010;
 8 int a[N],f[N];
 9 
10 int ffind(int l,int r,int x)
11 {
12     if (l==r) return l;
13     int mid=(l+r)>>1;
14     if (x>f[mid]) return ffind(mid+1,r,x);
15     else return ffind(l,mid,x);
16 }
17 int main()
18 {
19     int n,ans=0;
20     scanf("%d",&n);
21     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
22     f[++ans]=a[1];
23     for (int i=2;i<=n;i++)
24     {
25       int x;
26       if (a[i]>f[ans]) x=++ans;
27       else x=ffind(1,ans,a[i]);
28       f[x]=a[i];
29     }
30     printf("%d",ans);
31     return 0;
32 }
View Code 2

3.O(n log n)。(参考自蓝书 p62,挖了坑,没时间填了......)

1 for (int i=1;i<=n;i++) g[i]=INF;
2 for (int i=0;i<n;i++)
3 {
4     int k=lower_bound(g+1,g+n+1,A[i])-g;
5     d[i]=k;
6     g[k]=A[i];
7 }

 

posted @ 2016-09-25 16:27  konjac蒟蒻  阅读(509)  评论(0编辑  收藏  举报