【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 }
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 }
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 }