最长不降子序列的NlogN的解法

B3637 最长上升子序列 这是一个简单的动规板子题。给出一个由nn≤5000个不超过106的正整数组成的序列。请输出这个序列的的长度。最长上升子序列是指,从原序列中取出一些数字排在一起,这些数字是的。 阅读详情

这是一个很好的题目。题目的算法还是比较容易看出来的,就是求最长上升子序列的长度。不过这一题的数据规模最大可以达到40000,经典的O(n^2)的动态规划算法明显会超时。我们需要寻找更好的方法来解决是最长上升子序列问题。

先回顾经典的O(n^2)的动态规划算法:
设A[i]表示序列中的第i个数,F[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设F[i] = 0(i = 1, 2, ..., len(A))。则有动态规划方程:F[i] = max{1, F[j] + 1} (j = 1, 2, ..., i - 1, 且A[j] < A[i])。

现在,我们仔细考虑计算F[i]时的情况。假设有两个元素A[x]和A[y],满足

(1)x < y < I (2)A[x] < A[y] < A[i] (3)F[x] = F[y]

此时,选择F[x]和选择F[y]都可以得到同样的F[i]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢?

很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[i-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。

再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[i] = k的所有A[i]中的最小值。设D[k]记录这个值,即D[k] = min{A[i]} (F[i] = k)。

注意到D[]的两个特点:

(1) D[k]的值是在整个计算过程中是单调不上升的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。

利用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[i]与D[len]。若A[i] > D[len],则将A[i]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A[i];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[i]。令k = j + 1,则有D[j] < A[i] <= D[k],将A[i]接在D[j]后将得到一个更长的上升子序列,同时更新D[k] = A[i]。最后,len即为所要求的最长上升子序列的长度。

在上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

这个算法还可以扩展到整个最长子序列系列问题,整个算法的难点在于二分查找的设计,需要非常小心注意。

补充:
1:下面程序的F数组在此题中没有用到,它是用来保存每个从1--i的最长不降子序列。
2:此程序虽然很快,但是它却丢掉了普通算法的一些优势,就是:我们如果要把最长的子序列找出来,那么就是十分的困难的啦,因为我们把原有的数据在work的时候给覆盖了,其它的方面还是十分的满意。

【参考程序】:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int f[100001],a[100001],c[100001];
int n,size;
int bsearch(int ai)
{
int l=1,r=size,mid;
while (l<=r)
{//二分查找
mid=(l+r)>>1;
if (ai<c[mid-1] && ai>=c[mid]) return mid;
else if (ai<c[mid]) l=mid+1;
else r=mid-1;
}
}
int main()
{
scanf("%d",&n);
if (!n)
{
printf("0/n");
exit(0);
}
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
f[1]=1;c[1]=a[1];size=1;
for (int i=2;i<=n;i++)
{
if (a[i]==0) continue;
int j;
if (a[i]>=c[1]) j=1;
else if (a[i]<c[size]) j=++size;
else j=bsearch(a[i]);
f[i]=j;c[j]=a[i];
}
printf("%d/n",size);
system("pause");
return 0;
}
巨硬面试题:最长上升子序列(动态规划+二分优化思路) 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。 →在线做题地址 说明 最长上升子序列的定义: 最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。 维基百科→https://en.wikipedia.org/wiki/Longest_increasing_subsequence 样例 1: 输入: [5,4,1,2,3] 输出: 3 解释: LIS 是 [1,2,3] 样例 2: 输入: [4,2,4,5, 阅读详情

相关推荐

从零搭建AGV避障系统:我用激光雷达+树莓派视觉模块踩过的那些坑

本文分享了基于树莓派与国产激光雷达从零搭建低成本AGV避障系统的实战经验。针对复杂仓库环境,详细剖析了激光雷达数据噪声、视觉模块算力瓶颈、IMU漂移等核心挑战,并提供了多传感器融合、软件架构设计与避障决策的具体解决方案,为中小型自动化改造项目提供了宝贵参考。

git9versioner的博客 983

【转】nlogn的最长不降子序列(LIS)

O(nlogn)的算法关键是它建立了一个数组temp[],temp[i]表示长度为i的不下降序列中结尾元素的最小值,用top表示数组目前的长度,算法完成后top的值即为最长不下降子序列的长度。设当前的以求出的长度为top,则判断num[i]和temp[top]:1.如果num[i]>=temp[top],即num[i]大于长度为top的序列中的最后一个元素,这样就可以使序列的长度增...

CSND12333的博客 420

高光谱图像处理和分析软件(包含雷尼绍Renishaw wdf 文件导入解析功能)

是一款基于微软windows操作系统的多传感器成像工具,用于处理和分析高光谱图像。它是一个模块化的系统,由一个基本的引擎,一个图形用户界面,一个化学计量工具箱和可选的用户提供的模块组成。它支持最重要的光谱成像技术,如UV/Vis、红外、拉曼、太赫兹、光发射/吸收和质谱。除此之外,还允许用户将高光谱图像与物理属性图和传统高分辨率彩色照片合并在一起 链接:https://pan.baidu.com/s/1ruQ5YSh5_ZS9wx_V3Q5Oow 提取码:qdx0 ...

业余程序员小徐的博客 8065

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

用O(NlogN)的复杂度求 最长不下降子序列。深度挖掘数据下的规律,可以加速dp的推导。

说文科技,做有态度的研究。 6560

Oracle 11g RAC 节点通信故障排查与修复指南

本文详细介绍了Oracle 11g RAC集群发生节点通信故障时的系统性排查与修复方法。当出现节点异常重启时,核心问题往往源于心跳网络中断。文章提供了从快速诊断、日志分析到六层深度检查的完整指南,并给出了针对网络配置、内核参数及Oracle已知Bug等常见根因的实战解决方案,帮助DBA快速恢复集群稳定。

weixin_29246195的博客 270

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

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

weixin_39667626的博客 848

STM32H745xI双核芯片SMPS/LDO配置误区:从复位失败到精准排查的实战复盘

本文详细分析了STM32H745xI双核芯片在SMPS/LDO配置中的常见误区,特别是复位失败问题的排查过程。通过对比参考板与故障板的波形数据,揭示了电感选型不当导致的电源稳定性问题,并提供了硬件设计检查清单和调试技巧,帮助开发者避免类似陷阱。

weixin_42531588的博客 264

最长不下降子序列(复杂度较低的方法)

最长不下降子序列随笔 参考文章最长不下降子序列nlogn算法详解 最长不下降子序列是动态规划的基础题,通常我们会这样去解它 int a[MAXN], d[MAXN]; int dp() { d[1] = 1; int ans = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j < i; j++) if (a[j] <= a[i]) { d[i] = max(d[i], d[j] + 1)

skl4869的博客 491

使用Eggjs做服务端获取微信小程序云开发数据库的access_token

最近自己想写了一个微信小程序,恰巧里面有个云开发,恰巧里面有个数据库,那懒人的我,就直接用云开发里面的数据库了,做测试或者正式也好,对于刚开始来说,免费的配额是够用的。 后来想到在小程序云开发上面的数据怎么看才方便?刚好微信小程序有一个服务端API,那就方便很多了,那就直接敲个服务端来获取云开发上数据库的数据出来到自己的后台上面,那以后管理起来也方便很多。 看了一下服务端api之后,发现有三个重要...

kalenE992的博客 1704

基于CANopenNode协议栈在正点原子探索者开发板上进行移植

配置好之后进行采用keil IDE进行代码生成即可,还需要进行CANopenNode的代码集成,并参照demo进行代码移植。发送完成后心跳包以1s的周期重新开始发送。正点原子的开发板引出的是CAN1接口。这里的心跳时间间隔设置为100ms。则将心跳包周期设置为0。

wangfandashe的博客 4693

最长不下降子序列(n*logn)

用 f [ ] 数组记录的是长度为 i 时的末位数为f [ i ]。这样f [] 数组是个单调递增数列,可用二分查找末位小于等于当前数且最长的子序列。 ll find(ll le,ll ri,ll x) { while(le<=ri) { ll m=(le+ri)>>1; if(f[m]<=x) { le=m+1; }else { ri=m-1; }

KatnissJ的博客 781

Allegro如何用自带的功能将线段变成铜皮操作指导

在做PCB设计的时候,有时根据设计需要将线段变成铜皮,可以借助辅助工具来实现这一操作,但是Allegro自身也自带这个功能,如下图。勾选Delete original objects。如何用自带的功能将线段变成铜皮操作指导。勾选Convert to shape,需要把这段走线变成铜皮。

weixin_54787054的博客 2945

最长不降子序列 NlogN解法

这是一个很好的题目。题目的算法还是比较容易看出来的,就是求最长上升子序列的长度。不过这一题的数据规模最大可以达到40000,经典的O(n^2)的动态规划算法明显会超时。我们需要寻找更好的方法来解决是最长上升子序列问题。先回顾经典的O(n^2)的动态规划算法: 设A[i]表示序列中的第i个数,F[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设F[i] = 0(i = 1

蓝色污点的专栏 6227

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

DP_最长不下降子序列_ 外星人的密码数字

外星人的密码数字 **时间限制:**1000ms **内存限制:**65535KB **提交总数:**12 **通过数量:**6 题目描述 ​ XXXX年突然有外星人造访,但大家语言不通,不过科学家们经过研究发现外星人用26个英文字母组成的单词中最长不降子序列的长度来表述数字,且英文字母的排列顺序不同,现给出其排列顺序,再给出外星人说的每个数字(其实是每个英文...

ZRH的博客 619

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

算法 - 最长上升子序列

算法第三次上机A题 解法一 : 动态规划 思路: 将问题简化为相同求解方式但规模更小的子问题 1)n个数的最长上升子序列 = 前n-1个数的最长上升子序列 + 比较第n个数 2)前1个数的最长上升子序列 = 1 具体解法: 1)从第一个数开始,寻找以A[i]结尾的最长上升子序列d[i]。 2)寻找d[i]时,比较A[j]=A[1]到A[i-1],当A[j]<A[i],取d[i] 为d[i]和...

xx_yan的博客 558

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

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

二分+dp

阿正的梦工坊 4992

最长不降子序列问题

问题输入·设有由n个不相同的整数组成的数列,记为:a(1)、a(2)、……、a(n)且a(i)a(j) (ij)·若存在i1输出程序要求,当原数列给出之后,求出最长的不下降序列。例子数列3,18,7,14,10,12,23,41,16,24。3,18,23,24就是一个长度为4的不下降序列,同时也有3,7,10,12,16,24长度为6的不下降序列。

waterboom的专栏 846

最长上升子序列O(nlogn)算法核心思想简述

【题目描述】 给定N个数,求这N个数的最长上升子序列的长度。 【样例输入】 7 2 5 3 4 1 7 6 【样例输出】 4 下面记录本人对 “贪心 + 二分” 【O(nlogn)】 算法的一些理解。 0 本文思路: 1)介绍解题过程 2)分析解法可行性 不想听废话,请直接移步 第四部分 :“4 操作的意义”,演示该操作的效果与意义。 1 核心思想: (贪心)对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。 2 操作过程: 现创建一个新数组 b[ ], 用来存放:考虑

qq_43481884的博客 2762

最长不下降子序列思路及解法

最长不下降子序列学习日志

weixin_54777217的博客 1053

洛谷P1020导弹拦截——最长不升/上升子序列

洛谷P1020导弹拦截——最长不升/上升子序列

lijingheng66的博客 1307

HDU4604.Deque——nlogn求最长上升子序列的长度

http://acm.hdu.edu.cn/showproblem.php?pid=4604把一个序列中的元素放到队列里面,有3种操作,对于第i个元素 1.放到队头2.放到队尾3.舍弃 求最长上升序列的长度法1:求出每个以a[i]为起点的最长不升子序列的长度,和最长不降子序列的长度,两个相加再减去a[i]重复的次数法2:把当前序列复制两个,一个逆序,求总共的最长上升子序列的长度,然后奇偶避免重复

fsdcyr 679

LIS最长上升子序列

​ 定义数组dp[i]表示序列中,以的递增子序列的最长长度,由于当遍历到第 i 个数的时候,前 i-1 个数,以他们结尾的递增子序列的最长长度,已经处理好了,那么就可以经由他们推出dp[i]

qq_74289134的博客 347

LIS(最长上升子序列)问题的三种求解方法以及一些例题

摘要本篇博客介绍了求LIS的三种方法,分别是O(n^2)的DP,O(nlogn)的二分+贪心法,以及O(nlogn)的树状数组优化的DP,后面给出了4道LIS的例题。LIS的定义一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 <

George__Yu的博客 2万+

LIS(最长上升子序列) (n*log2*n)

li 给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中 的各个元素互不相同。求这两个序列的最长公共子序列的长度。 解法2:贪心+二分: 思路: 新建一个 low 数组,low [ i ]表示长度为i的LIS结尾元素的最小值。对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。因此,我们只需要...

weixin_30376453的博客 180
上一篇: Minimum Inversion Number
下一篇: Pku2352 Stars 树状数组
Hello______world
博客等级 码龄14年 16粉丝 106原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值