最长不下降子序列nlogn算法详解

今天花了很长时间终于弄懂了这个算法……毕竟找一个好的讲解真的太难了,所以励志我要自己写一个好的讲解QAQ

这篇文章是在懂了这个问题n^2解决方案的基础上学习。

解决的问题:给定一个序列,求最长不下降子序列的长度(nlogn的算法没法求出具体的序列是什么)

定义:a[1..n]为原始序列,d[k]表示长度为k的不下降子序列末尾元素的最小值,len表示当前已知的最长子序列的长度。

初始化:d[1]=a[1]; len=1; (0个元素的时候特判一下)

现在我们已知最长的不下降子序列长度为1,末尾元素的最小值为a[1],那么我们让i从2到n循环,依次求出前i个元素的最长不下降子序列的长度,循环的时候我们只需要维护好d这个数组还有len就可以了。

关键问题就是怎么维护?

可以看出我们是要用logn的复杂度维护的。实际上利用了d数组的一个性质:单调性。(长度更长了,d[k]的值是不会减小的)

考虑新进来一个元素a[i]:

  如果这个元素大于等于d[len],直接让d[len+1]=a[i],然后len++。这个很好理解,当前最长的长度变成了len+1,而且d数组也添加了一个元素。

  如果这个元素小于d[len]呢?说明它不能接在最后一个后面了。那我们就看一下它该接在谁后面。

    准确的说,并不是接在谁后面。而是替换掉谁。因为它接在前面的谁后面都是没有意义的,再接也超不过最长的len,所以是替换掉别人。那么替换掉谁呢?就是替换掉那个最该被它替换的那个。也就是在d数组中第一个大于它的。第一个意味着前面的都小于等于它。假设第一个大于它的是d[j],说明d[1..j-1]都小于等于它,那么它完全可以接上d[j-1]然后生成一个长度为j的不下降子序列,而且这个子序列比当前的d[j]这个子序列更有潜力(因为这个数比d[j]小)。所以就替换掉它就行了,也就是d[j]=a[i]。其实这个位置也是它唯一能够替换的位置(前面的替了不满足d[k]最小值的定义,后面替换了不满足不下降序列)

  至于第一个大于它的怎么找……STL upper_bound。每次复杂度logn。

至此,我们就神奇的解决了这个问题。按照这个思路,如果需要求严格递增的子序列怎么办?

仍然考虑新进来一个元素a[i]:

  如果这个元素大于d[len],直接让d[len+1]=a[i],然后len++。这个很好理解,当前最长的长度变成了len+1,而且d数组也添加了一个元素。

  如果这个元素小于等于d[len]呢?说明它不能接在最后一个后面了。那我们就看一下它该接在谁后面。

    准确的说,并不是接在谁后面。而是替换掉谁。因为它接在前面的谁后面都是没有意义的,再接也超不过最长的len,所以是替换掉别人。那么替换掉谁呢?就是替换掉那个最该被它替换的那个。也就是在d数组中第一个大于等于它的。第一个意味着前面的都小于它。假设第一个大于等于它的是d[j],说明d[1..j-1]都小于它,那么它完全可以接上d[j-1]然后生成一个长度为j的上升子序列,而且这个子序列比当前的d[j]这个子序列更有潜力(因为这个数比d[j]小)。所以就替换掉它就行了,也就是d[j]=a[i]。其实这个位置也是它唯一能够替换的位置(前面的替了不满足d[k]最小值的定义,后面替换了不满足上升序列)

  至于第一个大于等于它的怎么找……STL lower_bound。每次复杂度logn。

下面是最长不下降子序列的代码

//最长不下降子序列nlogn  Song 

#include<cstdio>
#include<algorithm>
using namespace std;

int a[40005];
int d[40005];

int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    if (n==0)  //0个元素特判一下
    {
        printf("0\n");
        return 0;
    }
    d[1]=a[1];  //初始化
    int len=1;
    for (int i=2;i<=n;i++)
    {
        if (a[i]>=d[len]) d[++len]=a[i];  //如果可以接在len后面就接上
        else  //否则就找一个最该替换的替换掉
        {
            int j=upper_bound(d+1,d+len+1,a[i])-d;  //找到第一个大于它的d的下标
            d[j]=a[i];
        }
    }
    printf("%d\n",len);
    return 0;
}

想了一晚上这个问题终于想通了。

时间: 2025-01-07 15:19:05

最长不下降子序列nlogn算法详解的相关文章

toj 4071 最长不下降子序列nlogn解法

题目描述:给出2D空间中的n只鸟的坐标,射手对其进行射击,要求射击的鸟的坐标越来越大,即对于第i和第i+1只鸟,要求满足:xi<=xi+1 && yi <= yi+1.求最多能射击多少只鸟. 思路:将所有点按照x坐标排序,x坐标相同则按照y坐标排序.则x方向上可以满足限制,对y方向上求最长不下降子序列即可.由于数据量较大,需要采取nlogn的优化算法. 1 #include <algorithm> 2 #include <iostream> 3 #inc

最长不下降子序列 nlogn &amp;&amp; 输出序列

最长不下降子序列实现: 利用序列的单调性. 对于任意一个单调序列,如 1 2 3 4 5(是单增的),若这时向序列尾部增添一个数 x,我们只会在意 x 和 5 的大小,若 x>5,增添成功,反之则失败.由于普通代码是从头开始比较,而 x 和 1,2,3,4 的大小比较是没有用处的,这种操作只会造成时间的浪费,所以效率极低.对于单调序列,只需要记录每个序列的最后一个数,每增添一个数 x,直接比较 x 和末尾数的大小.只有最后一个数才是有用的,它表示该序列的最大限度值. 实现方法就是新开一个数组 d

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

最长上升子序列 时间限制: 10 Sec   内存限制:128 MB 题目描述 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.我们想知道此时最长上升子序列长度是多少? 输入 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N) 输出 1行,表示最长上升子序列的长度是多少. 样例输入 3 0 0 2 样例输出 2 提示 100%的数据 n&l

最长上升子序列nlogn解法详解 poj 2533

先上一个n^2的算法: 1 #include <iostream> 2 using namespace std; 3 4 const int N = 1000; 5 int a[N]; 6 int g[N]; 7 8 int main () 9 { 10 int n; 11 while ( cin >> n ) 12 { 13 for ( int i = 0; i < n; i++ ) 14 { 15 cin >> a[i]; 16 g[i] = 1; 17 }

tyvj 1049 最长不下降子序列 n^2/nlogn

P1049 最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 最长不下降子序列的长度 测试样例1 输入 3 1 2 3 输出 3 备注 N小于5000for each num <=maxint 题意:中文题意 题解:不下降也就是>= n^n  dp[i] 表示前i个数的最长不下降子序列的长度 1 /*****************************

hdu 1025 Constructing Roads In JGShining&#39;s Kingdom(最长上升子序列nlogn算法)

学习了最长上升子序列,刚开始学的n^2的方法,然后就超时了,肯定超的,最大值都是500000,平方之后都12位 了,所以又开始学nlogn算法,找到了学长党姐的博客orz,看到了rating是浮云...确实啊,这些不必太关 注,作为一个动力就可以啦.没必要看的太重,重要的事学习知识. 思路: 这道题目可以先对一行排序,然后对另一行求最长上升子序列... n^2算法: 序列a[n],设一个数组d[n]表示到n位的时候最长公共子序列(此序列包括n),所以呢 d[n]=max(d[j]+1,0<j<

最长不下降子序列 O(nlogn)

1 #include<stdio.h> 2 int a[1000] , temp[1000] ; 3 int n , top ; 4 5 int binary_search (int x) { 6 int fir = 0 ; 7 int last = top ; 8 int mid ; 9 while (fir <= last ) { 10 mid = (fir + last) / 2 ; 11 if ( x <= temp[mid] ) { 12 last = mid - 1;

网络流24题之最长不下降子序列问题

P2766 最长不下降子序列问题 题目描述 ?问题描述: 给定正整数序列x1,...,xn . (1)计算其最长不下降子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列. (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列. ?编程任务: 设计有效算法完成(1)(2)(3)提出的计算任务. n<=500 输入输出格式 输入格式: 第1 行有1个正整数n,表示给定序列的长度.接下来的1 行有n个正整数n:x1, ...

【模板】最长不下降子序列

====接力dalao完成==== 前文链接:(CSP-S RP++!) 对前文的一些补充: 首先清楚最长不下降子序列是一个递增但是允许不同位元素相等的序列.而最长上升子序列则是一个单调递增的序列. 而两者都是子序列,所以子序列的长度一定小于等于原序列.且子序列在原序列的位置不一定连续. 这个O(nlogn)的算法使用的是贪心的思想. 为了帮助理解,请与以下代码对比阅读: #include<iostream> using namespace std; int a[1000001],dp[100