LIS的贪心优化算法(O(nlogn))

本文通过实例讲解了最长上升子序列(LIS)问题的nlogn优化算法过程,采用贪心策略并利用二分查找降低时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接https://www.cnblogs.com/Bravewtz/p/10624796.html

LIS的nlogn的优化:
LIS的优化说白了其实是贪心算法,比如说让你求一个最长上升子序列把,一起走一遍。

比如说(4, 2, 3, 1, 2,3,5)这个序列,求他的最长上升子序列,那么来看,如果求最长的上升序列,那么按照贪心,应该最可能的让该序列的元素整体变小,以便可以加入更多的元素。
现在开辟一个新的数组,arr[ 10 ], { .......} --> 这个是他的空间 ,现在开始模拟贪心算法求解最长上升子序列,第一个数是4,先加进去,那么为{ 4 }再来看下一个数2,它比4小,所以如果他与4替换是不是可以让目前子序列(他这一个元素组成的子序列)变得更小,更方便以后元素的加入呢?是的。所以现在为{ 2 } 再来看他的下一个元素3,他要比2大,所以呢加在他的后面,{ 2, 3}
再看下一个元素是1,它比3要小,所以呢为了保证子序列整体尽可能的小(以便可以加入更多的元素),从目前的序列中查找出第一个比他大的数替换掉,那么就变成了{ 1, 3},继续。。 下一个数是2,那么序列变为{ 1,2},再下一个数为3,那么序列为{1,2,3},在下一个数为5,那么序列为{1,2,3,5},完。 目前序列里又4个元素,所以他的最长子序列的个数为4,但是这个序列是一个伪序列,里面的元素,并不是真正的最长上升子序列,而仅仅和最长上升子序列的个数一样。因为查找的时候用的二分查找,所以时间复杂度为o(nlogn)。

复制代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 int main(){
 8     int arr[500],n,dp[500];
 9     scanf("%d",&n);
10     for( int i=1; i<=n; i++ ){
11         scanf("%d",&arr[i]);
12     }
13     int k=1;
14     dp[k]=arr[1];
15     for( int i=2; i<=n; i++ ){
16         if(arr[i]>dp[k]) dp[++k]=arr[i];
17         else *lower_bound(dp+1,dp+1+k,arr[i])=arr[i];
18     }
19     printf("%d\n",k);
20     return 0;
21 }

复制代码

 

有些目标看似很遥远,但只要付出足够多的努力,这一切总有可能实现!

### 关于最长递减子序列优化算法 #### 最长递减子序列的概念及其重要性 在计算机科学领域,最长递减子序列(Longest Decreasing Subsequence, LDS)是一个重要的概念,在动态规划和贪心算法中有广泛应用。LDS是指在一个给定的数值序列中找到一个尽可能长的子序列,使得这个子序列中的元素按照严格递减顺序排列。 #### 动态规划解法的时间复杂度分析 对于未经过任何优化的传统动态规划方法来解决这个问题时,时间复杂度通常为O(n^2),其中n表示输入序列的长度。这种方法通过构建一个二维表dp[][], dp[i][j]代表以第i个数结尾且最后一个数字小于等于nums[j]的最大递减子序列长度[^1]。 然而,当面对更大数据集的时候,这样的效率显然不够理想;因此出现了基于二分查找技术改进后的版本——即所谓的“带权值”的单调栈结构,它能够有效地降低计算量并提高性能表现至接近线性的程度(O(nlogn))。 #### 基于二分查找的C++代码实现 下面给出了一种利用STL库函数lower_bound()配合vector容器完成上述逻辑的具体实例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int lengthOfLIS(vector<int>& nums){ vector<int> temp; int ans = 0; for(auto num : nums){ auto it = lower_bound(temp.begin(),temp.end(),num,greater<int>()); if(it==temp.end()){ temp.push_back(num); ++ans; }else{ *it=num; } } return ans; } // 调整此函数名以便适应题目需求 (由升序改为降序) int longestDecreasingSubsequenceLength(vector<int>& nums){ reverse(nums.begin(), nums.end()); return lengthOfLIS(nums); } ``` 这段程序首先定义了一个辅助函数`lengthOfLIS()`用于处理标准形式下的最长上升子序列问题,接着创建了另一个名为`longestDecreasingSubsequenceLength()`的新功能模块,通过对原始数据进行反转操作从而间接实现了针对下降趋势情况的有效求解过程。 #### Python版高效解决方案 Python同样支持类似的思路来进行编码实践: ```python from bisect import bisect_left as bl def lis_length(arr): tails = [] size = 0 for val in arr: idx = bl(tails,val) if idx == len(tails): tails.append(val) else: tails[idx]=val if idx>=size:size+=1 return size def lds_length(seq): seq.reverse() res=lis_length(seq) seq.reverse() return res ``` 这里采用了内置工具包`bisect.bisect_left()`代替手动编写二分搜索部分,并且保持了整体框架的一致性和简洁性。 #### 应用场景探讨 这类算法不仅限于理论研究层面的应用价值,在实际项目开发过程中也有着广泛用途。例如,在金融数据分析里可用于识别股票价格走势中的连续下跌区间;或是作为某些特定类型游戏设计的核心机制之一,帮助玩家理解角色属性变化规律等等[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值