【模板】最长上升子序列(LIS)及其优化 & 洛谷 AT2827 LIS

最长上升子序列


传送门


题意

  对于给定的一个n个数的序列,找到它的一个最长的子序列,并且保证这个子序列是由低到高排序的。

例如,1 6 2 5 4 6 8的最长上升子序列为1 2 4 6 8。

基本思路

  非常显然,这类题用dp求解。 dp[i]表示已i为结尾的最长上升子序列的长度,首先枚举每一个末尾i,然后枚举从1到i-1,如果a[1...i-1]<a[i]说明满足上升子序列,就加上一后取max。

  附上代码。

 1 //LIS O(n^2)
 2 #include<iostream>
 3 using namespace std;
 4 int dp[100005],a[100005],n,maxx=-999;
 5 int main(){
 6     cin>>n;
 7     for(int i=1;i<=n;i++) cin>>a[i],dp[i]=1;
 8     for(int i=1;i<=n;i++){
 9         for(int j=1;j<i;j++){
10             if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1);//如果后面的大于前面的,就取较大的值。 
11         }
12         maxx=max(maxx,dp[i]);                    //每次更新最大值 
13     }
14     cout<<maxx;
15     return 0;
16 }
O(n^2)代码

优化

//咕了八个半月的优化。。。。

方法是把dp表示的意义倒过来:dp[i]表示长度为i的最长上升子序列的结尾元素最小是什么。

很显然,dp数组一定是单调递增的。

这时,对于每一个a[i],我们做出判断:

  • 当a[i]比当前最长的序列的最后一个数字还大,那么长度++;
  • 否则就二分查找dp数组中第一个>=a[i]的数值,并把它改为a[i]。

为什么呢?

因为这第一个大于等于a[i]的数的前一个数是最后一个小于a[i]的数,然后a[i]这个数就可以接到那个数的后面,又因为我们要保存的是最小值,就更新为a[i]。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=100005;
 5 int n,dp[maxn],a[maxn],len;
 6 int main()
 7 {
 8     cin>>n;
 9     for(int i=1;i<=n;i++) cin>>a[i];
10     for(int i=1;i<=n;i++){
11         if(a[i]>dp[len]) dp[++len]=a[i];
12         else{
13             dp[lower_bound(dp+1,dp+len+1,a[i])-dp]=a[i];
14         }
15     }
16     cout<<len;
17     return 0;
18 }
O(nlogn)代码
posted @ 2019-12-14 22:24  尹昱钦  阅读(737)  评论(0编辑  收藏  举报