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

====接力dalao完成====

前文链接:(CSP-S RP++!)

对前文的一些补充:

首先清楚最长不下降子序列是一个递增但是允许不同位元素相等的序列。而最长上升子序列则是一个单调递增的序列。

而两者都是子序列,所以子序列的长度一定小于等于原序列。且子序列在原序列的位置不一定连续。

这个O(nlogn)的算法使用的是贪心的思想。

为了帮助理解,请与以下代码对比阅读:

#include<iostream>
using namespace std;
int a[1000001],dp[1000001];
int ans;
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            if(a[j]<a[i])
            dp[i]=max(dp[j]+1,dp[i]);//暴力从原序列中找比当前位置小的数,并不断更新当前位置的最长序列值(比较好理解)
            //想不懂就。。。再想想吧//
        }
        ans=max(dp[i],ans);
    }
    cout<<ans<<endl;
    return 0;
} 

这个是O(n2)的暴力算法,大概思路就是每次选取一个终点,再暴力求出终点的最长序列值,再从答案中选取最大值。

而O(nlogn)的实现过程与这个正好相反。

因为每次暴力更新都会有很多不必要的比对,比如对于原序列

1 2 3 4 5

当前选定的终点是5,在上面的暴力程序中,对于位置5会与1,2,3,4各比较一次,然后得出最后答案。然而,因为这个队列已经是单调递增的,所以5只需要与前一位4比对一次就可以得出答案,从而省去前面3次无用的比对。

为了避免浪费时间,这里再开一个数组d来存储已经找出的性价比最高的最长不下降子序列。

这里的“性价比”是指如果采用当前这个子序列作为既定的开头,用于下面继续比对,这样得出的答案一定是最优的

举例来说:

对于原序列:

1 2 5 7 8 1 10

有下列子序列
a:1 5 7 8

b:1 2 5 8

称b的性价比高于a,其原理是,对于b数组中相邻两个元素的差要小于a数组中的,而且最后一个元素要比a数组的小,此时称b的性价比比a高。

而对于性价比更高的序列,再接着处理时,最终所得的结果是最优的。(有最优子结构

而对于任意一个位置,若在原序列中的a[i]>d[len],其中len为d的长度,那么d[len++]=a[i];

这个的原理很简单,但是当a[i]<d[len]时,应该怎么处理呢?

去寻找d数组中第一个第一个大于a[i]的数,让a[i]与该数互换位置,得到性价比更高的序列,这次操作的原因已经在上文中阐述过。

又因为在上述操作过程中,d数组是一个不下降序列,所以可以用STL中的upper_bound来简化过程。

(upper_bound在dalao的题解里已经有解释,再复制一下)

首先介绍两个STL,非常好用(用于解决这道题)

(球球你看看它,如果看不懂就先看算法再看它,超级省事的)

lower_bound与upper_bound

  • 使用二分的思想
  • (所以要求在一个有序的序列内(你乐意的话自己定义一种排序方式也行,但是要有序(不然你也不知道它会出来什么乱七八糟的)))
  • (默认为升序
  • 复杂度大致为 log n

用法:lower_bound(a+1,a+n+1,x)

返回 a 数组内 第一个大于等于 x 的数的指针

令 P = lower_bound(a+1,a+n+1,x)- a,a [ p ] 则 为第一个大于等于 x 的数

(如果你会指针的话)  * p = lower_bound(a+1,a+n+1,x)也是 第一个大于等于 x 的数

upper_bound 和它的用法差不多,除了返回的是第一个大于 x 的指针

(也就是求最大不下降子序列最大上升子序列的差别)

 若我们要求下降序列呢 ? 

我们可以写一个 cmp,或者使用 C++ 自带的 greater<>() (都在STL里)

(和 sort 写法差不多)(sort总该写过的)

lower_bound(a,a+1,x,cmp) / lower_bound(a,a+1,x,greater<>());

一个小小的问题:

是怎样保证d数组中的数在原数组中的下标也是递增的呢?

这个问题是不用考虑的,因为最后决定答案的是len,并不是d数组或是数组内的元素决定的,d数组内只是存储可行的最优解,用来优化答案。

这样问题就不大了(看不懂可以模拟几组数组来理解一下)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100001],d[100001],n,len;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    d[1]=a[1];
    len=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>d[len])
        d[++len]=a[i];
        else
        {
            int j=upper_bound(d+1,d+len+1,a[i])-d;
            d[j]=a[i];
        }
        for(int i=1;i<=n;i++)
         {
                cout<<d[i]<<" ";
         }
            cout<<endl;//这个是分段输出,帮助理解
    }
    cout<<len<<endl;
    return 0;
}

无注释的点这里:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100001],d[100001],n,len;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    d[1]=a[1];
    len=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>d[len])
        d[++len]=a[i];
        else
        {
            int j=upper_bound(d+1,d+len+1,a[i])-d;
            d[j]=a[i];
        }
    }
    cout<<len<<endl;
    return 0;
}

板子在这里(优化自己打)

应该就这样了(看不懂我也。。自己搜吧)

dalao友情提供的例题:(这个hin经典) (这个超级妙)

掰掰

CSP-S RP++!

原文地址:https://www.cnblogs.com/Daz-Os0619/p/11789226.html

时间: 2024-12-08 09:16:13

【模板】最长不下降子序列的相关文章

swust oj 585--倒金字塔(LIS最长不下降子序列)

题目链接:http://acm.swust.edu.cn/problem/585/ Time limit(ms): 3000 Memory limit(kb): 65535 SWUST国的一支科学考察队到达了举世闻名的古埃及金字塔. 关于金字塔的建造一直是一个未解之谜, 有着“西方史学之父”之称的希罗多德认为,金字塔的建造是人力和牲畜,花费20 年时间从西奈半岛挖掘天然的石头运送到埃及堆砌而成.也有不少人认为是外星人修建的.人们发现胡夫金字塔的经线把地球分成东.西两个半球,它们的陆地面积是相等的

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

今天花了很长时间终于弄懂了这个算法……毕竟找一个好的讲解真的太难了,所以励志我要自己写一个好的讲解QAQ 这篇文章是在懂了这个问题n^2解决方案的基础上学习. 解决的问题:给定一个序列,求最长不下降子序列的长度(nlogn的算法没法求出具体的序列是什么) 定义:a[1..n]为原始序列,d[k]表示长度为k的不下降子序列末尾元素的最小值,len表示当前已知的最长子序列的长度. 初始化:d[1]=a[1]; len=1; (0个元素的时候特判一下) 现在我们已知最长的不下降子序列长度为1,末尾元素

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 1160 FatMouse&#39;s Speed(最长不下降子序列+输出路径)

题意: FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the s

【DP】最长不下降子序列问题(二分)

Description 给你一个长度为n的整数序列,按从左往右的顺序选择尽量多的数字并且满足这些数字不下降. Thinking 朴素dp算法:F[i]表示到第i位为止的最长不下降子序列长度 F[i]=max(F[j])+1,其中(j<i且a[j]<=a[i]) 时间复杂度:O(n2) 考虑维护一个队列g,用g[i]表示长度为i的最长不下降子序列结尾的最小值.根据g[i]的单调性,可以用二分查找的方法快速找到以当前数a[i]结尾的最长不下降子序列. Code 1 #include<cstd

O(n log n)求最长上升子序列与最长不下降子序列

考虑dp(i)表示新上升子序列第i位数值的最小值.由于dp数组是单调的,所以对于每一个数,我们可以二分出它在dp数组中的位置,然后更新就可以了,最终的答案就是dp数组中第一个出现正无穷的位置. 代码非常简单: for(int i=0;i<n;i++)dp[i]=oo; for(int i=0;i<n;i++)*lower_bound(dp,dp+n,A[i])=A[i]; printf("%d\n",(lower_bound(dp,dp+n,oo)-dp)); 如果是最长不

最长不下降子序列//序列dp

最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 最长不下降子序列的长度 测试样例1 输入 3 1 2 3 输出 3 备注 N小于5000for each num <=maxint 由N小于5000可知可以使用蛋疼的平方算法. 那么首先,我们都知道对于一个数列来讲,不下降子序列最短的的长度肯定是1. 那么我们设置一个f[i],表示以第i个数为结尾的最长不下降

最长不下降子序列的长度

试题描述 求最长不下降子序列的长度. 设有由n个不相同的整数组成的数列,记为:a[1].a[2].…….a[n].例如:3,18,7,14,10,12,23,41,16,24.若存在0<i1<i2<i3< … < ie 且有a[i1]<=a[i2]<= … <=a[ie]则称为长度为e的不下降序列.如上例中3,18,23,24就是一个长度为4的不下降序列,同时也有3,7,10,12,16,24长度为6的不下降序列. 输入 第一行为n,表示序列中整数的个数,第

SPOJ 3943 - Nested Dolls 最长不下降子序列LIS(二分写法)

现在n(<=20000)个俄罗斯套娃,每个都有宽度wi和高度hi(均小于10000),要求w1<w2并且h1<h2的时候才可以合并,问最少能剩几个. [LIS]乍一看跟[这题]类似,但是仔细看是有区别的,其实就相当于上一题多次求LIS,每次求完LIS后把得到的序列删去,然后重新求LIS,最后输出求LIS的次数,我一开始这样写,果然就TLE了.还是要另辟蹊径. 首先用贪心思想,先按照wi从大到小排序,wi相等的情况下hi从小到大,然后求最长不下降子序列(注意可以等于).输出其长度即可. 想