LIS

方法一:O(n2)的dp

  状态定义:dp[i] 表示以数 a[i] 结尾的 LIS 值

  状态转移方程:dp[i] = max(dp[j] + 1, dp[i]) 1 <= j < i && a[i] > a[j]

  代码:

 1 #include<iostream>
 2 using namespace std;
 3 const int maxn = 110;
 4 int a[maxn], dp[maxn];
 5 int n, ans = -1;
 6 int main()
 7 {
 8     cin >> n;
 9     for(int i = 1; i <= n; i++)
10     {
11         cin >> a[i];
12         dp[i] = 1;
13     }
14     for(int i = 1; i <= n; i++)
15         for(int j = 1; j < i; j++)
16             if(a[j] < a[i])
17                 dp[i] = max(dp[j] + 1, dp[i]);
18     for(int i = 1; i <= n; i++)
19         ans = max(ans, dp[i]);
20     cout << ans << endl;
21     return 0;
22 }

 

方法二:O(nlogn)的贪心+二分

  引入一个数组,暂且命名为 low[] ,它的含义是:low[i] 表示长度为 i 的 LIS 序列的最小的最后一个元素。所以,low 数组里的元素都是递增的,对于一个递增的序列,要使它“成长的潜力”尽可能大,那么它目前的最后一个元素就要尽可能小。这个思路是正确的,那么就是 low 数组的维护问题了,当目前遍历到的这个数 a[i] 比 low 数组尾部数字大的时候,就把目前这个数加入到 low 数组尾部,否则,在low数组中找到第一个大于等于 a[i] 的元素 low[j] ,用 a[i] 去更新 low[j]。这里就存在着时间的优化,如果你找第一个大于等于 a[i] 的元素时,从头到尾遍历,那么最终的时间复杂度还是O(n2),这里可以用二分来找,二分的复杂度是O(logn),所以总的时间复杂度就是O(nlogn)。

  看看代码吧:

 1 #include <iostream>
 2 using namespace std;
 3 const int maxn = 100010;
 4 const int INF = 0x3f3f3f3f;
 5 int low[maxn], a[maxn];
 6 int n, len;
 7 int binary_search(int *a, int r, int x)
 8 {
 9     int l = 1, mid;
10     while(l <= r)
11     {
12         mid = (l + r) >> 1;
13         if(a[mid] <= x)
14             l = mid + 1;
15         else
16             r = mid - 1;
17     }
18     return l;
19 }
20 int main()
21 {
22     cin >> n;
23     for(int i = 1; i <= n; i++)
24     {
25         cin >> a[i];
26         low[i] = INF;
27     }
28     low[1] = a[1];
29     len = 1;
30     for(int i = 2; i <= n; i++)
31     {
32         if(a[i] >= low[len])
33             low[++len] = a[i];
34         else
35             low[binary_search(low, len, a[i])] = a[i];
36     }
37     cout << len << endl;
38     return 0;
39 }

 

方法三:O(nlogn)的树状数组优化dp

  关于这个方法,其实整体思路和第一种没区别,只是在找数 a[i] 前面的LIS最大值的时候使用树状数组结构来优化效率。比如原序列 a[] ,复制一个一样的序列 b[] ,对 b[] 进行排序和去重(不去重的话就不是严格的上升序列),然后遍历 a[] 数组(因为子序列是建立在原序列的基础上的),假设我们遍历到了 a[i] ,我们要得到原序列中在 a[i] 前面的数其中LIS值最大的那个且那个数要比当前 a[i] 小,这两者缺一不可,那么好,b[] 数组刚好是排序后的数,找到数 a[i] 在 b[] 中的位置下标 p ,那么也就是要在 b[] 数组中下标从1到p-1(假设我们的数下标都是从1开始的)中找到LIS值最大的,这里这个操作就可以用树状数组的区间查询来实现,设为 query(p-1) ,则 ans = max(ans,query(p-1)+1) 就能得到最终的LIS。

Reference:

https://blog.csdn.net/lxt_Lucia/article/details/81206439

https://www.cnblogs.com/Rosebud/p/9845935.html

posted @ 2019-01-21 14:39  Piccolo_Devil  阅读(258)  评论(0编辑  收藏  举报