时间复杂度为O(nlogn)的LIS算法

时间复杂度为 n*logn的LIS算法是用一个stack维护一个最长递增子序列

如果存在 x < y 且  a[x] > a[y],那么我们可以用a[y]去替换a[x]

因为a[y]比较小,具有更大的潜力,使得后面的元素和它成为更长的递增序列

如例子: a[] = {1,4,8,3,6};

我们用一个stack st保存当前的最长递增子序列,top = 0;

很明显,初始化时st[top] = 1;

之后随着i循环变量的递增,如果

a[i] > st[top] , 那么 st[++top] = a[i]. 很显然top+1就是当前最长递增子序列的长度

这样子判断的时间复杂度是O(1),

为什么可以这样子判断???

因为st保存的是当前的最长递增子序列,所以如果a[i] > st[top] 那么a[i]可以加入st中

从而形成更长的最长递增子序列。

那么可能会有想法是,如果数据是1 5 3 4,很明显,最长递增子序列是1 3 4,

但是根据上面的想法是 1 5 形成最长递增子序列。

别担心

下面介绍 当  a[i] < st[top]时的处理方法

上面我们说过, 如果存在x < y 且 a[x] > a[y] 我们可以使用a[y]去替换a[x]

因为a[y] 具有更大的潜力,使得后面的元素和它成为更长的递增序列。

所以当 a[i] < st[top]时, 显然 st中的元素就是a[x],而a[i]就是a[y]

我们在st中使用二分查找找到第一个大于等于a[i]的元素,然后用a[i]去替换它

比如 st = 1 , 4 , 8时

a[i] = 3,

我们可以用a[i]去替换4,从而在不影响结果的前提下,减少时间复杂度

题目uva10534

给定一个一组数字,要我们求这样一个序列

在序列的左边是递增的,右边是递减的,且递增和递减的个数要是一样的

思路:分别从两边进行最长递增子序列的dp,

dp1是从下标 0 -> n-1   进行dp

dp2是从下标 n-1 -> 0   进行dp

所以 ans = max{ min(dp1[i]-1, dp2[i]-1)+1, 0<=i<n };

但是题目的数据有1w,O(N*N)的算法是不行的,

所以要用nlogn的算法

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <algorithm>
 5 #include <iostream>
 6 #include <queue>
 7 #include <stack>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 #include <string>
12 using namespace std;
13 typedef long long LL;
14 const int INF = 1<<30;
15 const int N = 10000 + 10;
16 int min(const int &a, const int &b)
17 {
18     return a < b ? a :b;
19 }
20 int max(const int &a, const int &b)
21 {
22     return a < b ? b : a;
23 }
24 int st[N];
25 int top;
26 void LIS(int *a, int n, int *dp)
27 {
28     int i,j;
29     top = 0;
30     st[top] = a[0];
31     for(i=1; i<n; ++i)
32     {
33         if(a[i] > st[top])
34         {
35             st[++top] = a[i];
36             dp[i] = top +1 ;
37         }
38         else
39         {
40             int low = 0, high = top;
41             while(low <= high)
42             {
43                 int mid = (low + high) >> 1;
44                 if(st[mid]<a[i])
45                     low = mid + 1;
46                 else
47                     high = mid - 1;
48             }
49             st[low] = a[i];
50             dp[i] = low +1;
51         }
52     }
53 }
54 int a[N];
55 int dp[2][N];
56 int main()
57 {
58     int n,i,j;
59     while(scanf("%d",&n)!=EOF)
60     {
61         for(i=0; i<n; ++i)
62         {
63             scanf("%d",&a[i]);
64             dp[0][i] = dp[1][i] = 1;
65         }
66         LIS(a,n,dp[0]);
67
68         int low = 0,high = n - 1;
69         while(low < high)
70         {
71             int t = a[low];
72             a[low] = a[high];
73             a[high] = t;
74             low ++;
75             high --;
76         }
77         LIS(a,n,dp[1]);
78
79         int ans = 0;
80         for(i=0; i<n; ++i)
81         {
82             int t = 2 * min(dp[0][i]-1,dp[1][n-i-1]-1) +1;//因为第二次dp是将数组倒过来dp,所以要n-i-1
83             ans = max(ans,t);
84         }
85         printf("%d\n",ans);
86     }
87     return 0;
88 }


时间: 2024-10-13 20:42:07

时间复杂度为O(nlogn)的LIS算法的相关文章

平均时间复杂度为O(nlogn)的排序算法

本文包括 1.快速排序 2.归并排序 3.堆排序 1.快速排序 快速排序的基本思想是:采取分而治之的思想,把大的拆分为小的,每一趟排序,把比选定值小的数字放在它的左边,比它大的值放在右边:重复以上步骤,直到每个区间只有一个数.此时数组已经排序完成. 快速排序最重要的是partition函数功能的实现,也就是将比选定基数小的值放在他的左边,比选定基数大的值放在它的右边的功能函数. 熟悉快速排序的人也许都会有自己的partition函数的写法.此处,提供两种不同的partition函数写法. 例1:

[BZOJ 1046][HAOI 2007]上升序列(nlogn的LIS算法)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1046 有人说这题是NOIP难度?表示怀疑,蒟蒻认为此题难度略大于NOIP.... 这个题的序列长度n<=1e4,如果用n^2的LIS做法肯定TLE,只能用nlogn的算法,这种算法在http://www.slyar.com/blog/longest-ordered-subsequence.html中有详细讲解. 由于题目题意要求,我们需要求出以每个数字开头的最长上升子序列长度,但

比较排序算法时间复杂度下界为nlogn的证明

比较排序算法的时间复杂度是O(nlogn)的证明: 排序算法的比较是两两进行的,所以可以抽象成一棵二叉树,相互比较的数分别是左右叶子结点,,比较的结果存储在父节点中,依此类推.那么算法的时间复杂度就是取决于树的深度.如果要对n个数字进行比较排序,则需要进行n!次,即该二叉树有n!片叶子. 一棵深度为d的二叉树拥有的叶子结点数最大为2d个,则具有n!片叶子的二叉树的深度为logn!. logn!=logn+log(n-1)+log(n-1)+…+log(2)+log(1)≥logn+log(n-1

hdu1025 Constructing Roads In JGShining&#39;s Kingdom (nlogn的LIS)

题目链接 第一次写nlogn复杂度的LIS,纪念一下. 题目意思是说.有两条平行线,两条平行线都有n个城市,都是从左到右标记为1--n,一条线上是富有城市,一个是贫穷城市.输入n,接下来有n行,p,r表示穷城市p和富有城市r 之间可以建一条路(p的顺序是1--n,一个贫穷城市只对应一个富有城市(弱爆的语文描述能力T_T)),公路不能交叉. 问最多可以建多少条公路. 在别处看到的对nlogn解法的解释吧算是: 时间复杂度:(NlogN): 除了算法一的定义之外,增加一个数组b,b[i]用以表示长度

hdu1025 Constructing Roads In JGShining&amp;#39;s Kingdom (nlogn的LIS)

pid=1025">题目链接 第一次写nlogn复杂度的LIS,纪念一下. 题目意思是说.有两条平行线,两条平行线都有n个城市,都是从左到右标记为1--n,一条线上是富有城市,一个是贫穷城市.输入n,接下来有n行,p,r表示穷城市p和富有城市r 之间能够建一条路(p的顺序是1--n,一个贫穷城市仅仅相应一个富有城市(弱爆的语文描写叙述能力T_T)).公路不能交叉. 问最多能够建多少条公路. 在别处看到的对nlogn解法的解释吧算是: 时间复杂度:(NlogN): 除了算法一的定义之外.添加

ACM:递归与分治,最大连续和,O(n3), O(n2), O(nlogn), O(n) 算法。

题目,求一个连续的数组,最大连续和. (一)O(n3)算法: 利用穷举法的思想,这种方法的效率最差. 代码如下: #include <iostream> #include <cstdlib> #include <ctime> #include <cmath> using namespace std; const int MAXN = 1000; int A[MAXN], n; int maxsum(int *A, int n) { int beat = A[

java实现LIS算法,出操队形问题

假设有序列:2,1,3,5,求一个最长上升子序列就是2,3,5或者1,3,5,长度都为3. LIS算法的思想是: 设存在序列a. ① 如果只有一个元素,那么最长上升子序列的长度为1: ② 如果有两个元素,那么如果a[1]>a[0],则最长上升子序列的长度为2,a[1]为该最长上升子序列的最后一个元素;若a[1]<a[0],则最长上升子序列的长度为1,a[0]和a[1]均为  其最长上升子序列的最后一个元素. ③ 如果由三个元素,那么如果a[2]>a[0],a[2]>a[1],则a[

最长上升子序列算法(n^2 及 nlogn) (LIS)

问题描述: 一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N.比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等.这些子序列中最长的长度是4,比如子序列(1, 3, 5

最长上升子序列 (LIS算法(nlong(n)))

设 A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 0(t = 1, 2, ..., len(A)).则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] < A[t]). 现在,我们仔细考虑计算F[t]时的情况.假设有两个元素A[x]和A[y],满足 (1)x < y < t (2)A[x] < A[y] < A[t] (3)F[x] =