最长不下降子序列1

本文介绍了最长不下降子序列的概念及其求解方法,并通过具体实例进行解释。此外还提供了练习题供读者实践,帮助深入理解这一算法。

前言
“洗澡中,请勿打扰,偷窥请购票,个体四十,团体八折!”好了,先不洗澡了,嘟嘟老师今天来讲一下——诶等一下,嘟嘟老师还没穿内裤呢,等一分钟……
啊,终于换好了,开始讲吧。
欢迎来到嘟嘟课堂。今天,嘟嘟老师给大家讲一个比较水的专题,也是被众多信息学爱好者所研究、追求的一个东东——最长不下降子序列!!!大家应该知道我们学校(纪中)的Farmer John(也就是在USACO题库中常常出现的农夫约翰)陈启峰。他老人家(嗯,28岁)出名的研究专利给大家列举其中的冰山一角:

网络流改成启峰流
SBT优化
Scapegoat树
优化最长不下降子序列(陈启峰把最长不降子序列O(n^2)的算法优化成了O(n log n)的算法)
……

So,大家应该知道今天要讲什么了吧(不是讲怎么穿内裤啊)。没错,就是大名鼎鼎的最长不下降子序列!!!
(注:代码和思路在嘟嘟老师“最长不下降子序列2”中,本篇博客仅作铺垫及引入)
题目描述http://172.16.0.132/junior/#main/show/1121
有长度为N的序列:
A1 A2 …..An
求最长不下降子序列:Ai1,Ai2,,,,,Aik, 其中ai1<=ai2<=…..<=aik
求最长不下降子序列的长度
样例输入
11
1 3 6 3 4 7 5 7 6 7 8
样例输出
8
讲解
何为“最长不降子序列”?
设有由n个不相同的整数组成的数列,记为:a(1)、a(2)、……、a(n)且a(i)<>a(j) (i<>j)
例如3,18,7,14,10,12,23,41,16,24。
上例中3,18,23,24就是一个长度为4的不下降序列,同时也有3,7,10,12,16,24长度为6的不下降序列。
也就是说,在a数组中按顺序找k个数,使这个第k个数大于等于第k-1个数且k的值最大,就成为长度为k的最长不下降子序列。
大家自己先去试一下吧。
如果做完了这道例题,嘟嘟老师这里还有几道题,最长不下降子序列是很重要的内容,大家多加理解练习。
练习1
http://172.16.0.132/junior/#main/show/1857
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统 V1.0。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够达到任意的高度,但是以后每一发炮弹都不能高于前一发的高度,这样会大大降低导弹的拦截率。
经过技术人员的苦心改良,导弹拦截系统升级为 V2.0,使得这个拦截系统在拦截过程中,有若干次机会提升到任意高度,虽然次数有限,但是已经算很大的改进了。
某天,雷达捕捉到敌国的导弹来袭,导弹 V2.0拦截系统接受考验的时刻开始来临了。
输入
第一行为整数N (0<=N<=20),M(1<=M<=50),N代表导弹拦截系统在拦截过程中,可以提升到任意高度的次数;M代表需要拦截的导弹的数目。N、M两个整数用空格隔开。
第二行为M个正整数,每个整数间使用若干空格隔开,分别表示导弹依次飞来的高度,雷达给出高度数据是不大于1000的正整数。
输出
输出一个正整数,代表该导弹拦截系统最多可以拦截的导弹数目。
样例输入
1 7
300 250 275 252 200 138 245
样例输出
6
数据范围限制
N (0<=N<=20),M(1<=M<=50)
练习2
http://172.16.0.132/junior/#main/show/1123
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例输入
389 207 155 300 299 170 158 65
样例输出
6(最多能拦截的导弹数)
2(要拦截所有导弹最少要配备的系统数)
嘟嘟老师也自己出了一道题:
猥琐的慢羊羊(本题及本帖为嘟嘟老师原创,不可转载,违者必究)
题目描述
羊村刚考完试……
由于最近喜羊羊等羊都在调戏美羊羊和红太狼(还有李子羊),无心于学业,考的都非常差。
但慢羊羊校长要看他们的成绩单,且必须从羊1看到羊n,按照顺序来。为了使慢羊羊校长冷静下来,喜羊羊想到了一个计策:在成绩单中去掉几个数,使A1.A2A3…AN中AI总是大于等于AI-1,这样慢羊羊校长看着分数上升,心情才会好些。
这个问题交给了你,请你来完成。
输入
输入共两行。
第一行有一个整数n,表示成绩单上共有n只羊的成绩。
第二行有n个数,表示每只羊的成绩。
输出
输出仅一行。
输出一个数,表示成绩单经过筛选后,最多还能保留的羊的数量。
样例输入
11
1 3 6 3 4 7 5 7 6 7 8
样例输出
8
数据范围限制
100%的数据,1<=n<=100000

好了,大家赶快去作这四题。正解详见“最长不降子序列2”
……

### 使用单调队列实现最长下降子序列算法 单调队列是一种高效的优化方法,可以将原本复杂度为 \( O(n^2) \) 的动态规划算法优化到 \( O(n \log n) \)[^1]。以下是使用单调队列求解最长下降子序列(LIS)的详细算法实现。 #### 算法思想 在求解最长下降子序列时,动态规划的核心是维护一个数组 `dp`,其中 `dp[i]` 表示以第 `i` 个元素结尾的最长下降子序列的长度。然而,这种方法的时间复杂度较高,因此需要借助单调队列进行优化[^4]。 单调队列优化的核心思想是:对于一个单调递增的序列,只需要记录每个长度对应的最小值即可[^2]。具体来说: - 使用一个数组 `f` 来存储当前可能成为 LIS 的候选序列。 - 遍历输入序列中的每个元素,通过二分查找确定该元素在 `f` 中的位置,并更新 `f` 的值。 #### 实现步骤 以下是基于单调队列的最长下降子序列算法的 Python 实现: ```python def lis_with_monotonic_queue(nums): if not nums: return 0 # 定义一个列表 f,用于存储当前的 LIS 候选序列 f = [] for num in nums: # 使用二分查找找到 num 在 f 中的插入位置 left, right = 0, len(f) while left < right: mid = (left + right) // 2 if f[mid] <= num: # 找到第一个大于 num 的位置 left = mid + 1 else: right = mid # 如果 left 等于 len(f),说明 num 比 f 中的所有数都大,直接追加 if left == len(f): f.append(num) else: # 否则替换掉第一个大于等于 num 的数 f[left] = num # 最终 f 的长度即为最长下降子序列的长度 return len(f) # 示例用法 nums = [11, 12, 13, 9, 8, 17, 19] print(lis_with_monotonic_queue(nums)) # 输出 5 ``` #### 代码解析 1. **初始化**:创建一个空列表 `f`,用于存储当前的 LIS 候选序列。 2. **遍历输入序列**:对每个元素 `num`,通过二分查找找到其在 `f` 中的插入位置。 3. **更新候选序列**: - 如果 `num` 大于 `f` 中的所有元素,则将其追加到 `f` 的末尾。 - 否则,替换掉 `f` 中第一个大于等于 `num` 的元素。 4. **返回结果**:最终 `f` 的长度即为最长下降子序列的长度。 #### 时间复杂度分析 - **二分查找**:每次查找的时间复杂度为 \( O(\log n) \)。 - **总复杂度**:由于需要对每个元素进行一次二分查找,因此总时间复杂度为 \( O(n \log n) \)[^1]。 #### 注意事项 1. 单调队列优化适用于严格递增或递减的子序列问题。 2. 如果需要输出具体的最长下降子序列,可以通过额外的回溯操作实现[^3]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值