LIS最长上升子序列O(nlogn)算法

Posted thesilvermoon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LIS最长上升子序列O(nlogn)算法相关的知识,希望对你有一定的参考价值。

正常的求LIS的方法是用dp来做,时间复杂度为O(n^2),但是面对一些题目的时候这个复杂度就有点高了,就去学了一下nlogn的解法。主要运用到了二分查找,stl里面的lower_bound 也可以。

upper_bound(i) 返回的是键值为i的元素可以插入的最后一个位置(上界) 
lower_bound(i) 返回的是键值为i的元素可以插入的位置的第一个位置(下界)

先贴一发O(n^2)的代码

 1 #include <bits/stdc++.h>
 2 #define fi first
 3 #define se second
 4 #define pb push_back
 5 #define fio ios::sync_with_stdio(false);cin.tie(0);
 6 #define pii pair<int,int>
 7 #define vi vector<int>
 8 #define vc vector<char>
 9 #define mii map<int,int>
10 #define si(a) scanf("%d",&a)
11 #define ss(a) scanf("%s",&a)
12 #define sl(a) scanf("%I64d",&a);
13 #define slf(a) scanf("%lf",&a);
14 #define CLEAR(a,b) memset(a,b,sizeof(a))
15 #define pi acos(-1)
16 
17 const int INF=0x3f3f3f3f;
18 const int N=2e5+5;
19 
20 typedef long long ll;
21 typedef double db;
22 typedef unsigned long long ull;
23 using namespace std;
24 int dp[N];
25 int num[N];
26 int n;
27 
28 void solve()
29 {
30     int res=0;
31     for (int i=0;i<n;i++)
32     {
33         dp[i]=1;
34         for (int j=0;j<i;j++)
35         {
36             if(num[j]<num[i])
37             {
38                 do[i]=max(dp[i],dp[j]+1);
39             }
40             res=max(res,dp[i]);
41         }
42     }
43     cout<<res<<endl;
44 }
45 int main()
46 {
47     fio;
48     int n;
49     cin>>n;
50     for (int i=0;i<n;i++)
51     {
52         cin>>num[i];
53     }
54     solve();
55 }

这个很好理解,对于O(nlogn)的解法我看了一些博客,里面比较重要的就是替换这个操作,每次找到一个比最后一个还要大的值的时候直接加入后面,碰到比最后面小的数字的时候往里面找第一个比它小的数字,替换掉后面一个。(语文不好。。。直接看例子吧)

如2 5 3 4 9 7 8 10 很容易就能看出来它的LIS长度为6,最长的是 2 3 4 7 8 10。 

具体步骤如下,2进入;5比2大,进入;此时因为3比5小,往里找,2比3小,3替换掉5;4比3大,4进入;9比4大,9进入;7比9小,往前找,4比7小,7替换掉9;后面都直接进入,得到LIS序列2 3 4 7 8 10。

O(nlogn)的代码具体如下

 1 #include <bits/stdc++.h>
 2 #define fi first
 3 #define se second
 4 #define pb push_back
 5 #define fio ios::sync_with_stdio(false);cin.tie(0);
 6 #define pii pair<int,int>
 7 #define vi vector<int>
 8 #define vc vector<char>
 9 #define mii map<int,int>
10 #define si(a) scanf("%d",&a)
11 #define ss(a) scanf("%s",&a)
12 #define sl(a) scanf("%I64d",&a);
13 #define slf(a) scanf("%lf",&a);
14 #define CLEAR(a,b) memset(a,b,sizeof(a))
15 #define pi acos(-1)
16 
17 const int INF=0x3f3f3f3f;
18 const int N=2e5+5;
19 
20 typedef long long ll;
21 typedef double db;
22 typedef unsigned long long ull;
23 using namespace std;
24 int ans[N];
25 int num[N];
26 
27 int main()
28 {
29     fio;
30     int n;
31     cin>>n;
32     for (int i=1;i<=n;i++)
33         cin>>num[i];
34     ans[1]=num[1];
35     int len=1;
36     for(int i=2;i<=n;i++)
37     {
38         if(num[i]>ans[len])
39             ans[++len]=num[i];
40         else
41         {
42             int pos=lower_bound(ans,ans+len,num[i])-ans;
43             ans[pos]=num[i];
44         }
45     }
46     for (int i=1;i<=len;i++)
47     {
48         cout<<ans[i]<<" ";
49     }
50     cout<<endl;
51 }
52 
53 
54 /*
55 8
56 2 5 3 4 9 7 8 10
57 */

技术分享图片


以上是关于LIS最长上升子序列O(nlogn)算法的主要内容,如果未能解决你的问题,请参考以下文章

最长上升子序列问题 nlogn 实现算法的简述

最长上升子序列 (LIS) 详解+例题模板 (全)(转)

最长上升子序列 (LIS) 详解+例题模板 (全)(转)

LIS的优化算法O(n log n)

(转载)最长递增子序列 O(NlogN)算法

最长上升子序列(LIS)题目合集