最长上升子序列O(nlog(n))

本文介绍了一种寻找序列中最长上升子序列的有效算法。通过扫描输入序列并利用二分查找来更新候选序列,实现了O(n log n)的时间复杂度。

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

思路:从头到尾扫一遍序列,用一个数组ans保存当前最长的上升序列,如果扫到的元素大于当前上升序列的最后一个,那么就把该元素加在ans序列末尾,否则,将该元素替换ans序列中第一个比该元素大的元素,如果没有则替换最后一个。替换过程使用二分处理。

#include <string.h>
#include <stdio.h>

int num[1005];
int ans[1005];
int len;

int search(int i)
{
    int left = 0,right = len,mid;
    while (left < right)
    {
        mid = left + (right - left) / 2;
        if (ans[mid] >= num[i]) right = mid;
        else left = mid + 1;
    }
    return left;
}

int main()
{
    int n;
    while (scanf("%d",&n) != EOF)
    {
        for (int i = 1; i <= n; i++)
            scanf("%d",&num[i]);
        ans[1] = num[1];
        len = 1;
        for (int i = 2; i <= n; i++)
        {
            if (num[i] > ans[len]) ans[++len] = num[i];
            else
            {
                int pos = search(i);
                ans[pos] = num[i];
            }
        }
        printf("%d\n",len);
    }
    return 0;
}
<think>嗯,用户想找最长不下降子序列的算法或代码实现。首先,我需要确认最长不下降子序列(Longest Non-DecreasingSubsequence,LNDS)和最长递增子序列(LIS)的区别。LNDS允许元素相等,而LIS是严格递增的。所以之前的引用里提到的LIS方法可能需要调整比较条件。用户可能已经知道LIS的常规解法,比如动态规划O()和贪心+二分O(nlog n)。但LNDS的贪心+二分方法可能需要修改二分查找的条件,允许等于的情况。比如在维护一个单调递增的数组时,当遇到相等的元素,可以替换掉第一个大于当前元素的位置,而不是严格大于的情况。然后,我需要参考用户提供的引用。引用3提到最长不下降子序列的思路,用尾部数组优化,通过比较新元素和各个序列的最后一个元素。这应该对应贪心+二分的方法。所以可能需要调整二分查找的逻辑,将条件从“严格大于”改为“大于等于”。 接下来,要考虑如何用代码实现。比如在Python中,维护一个数组tails,遍历每个元素,用bisect_right来找到插入的位置,这样当元素相等时会被放在右边,保持不下降的特性。或者可能需要自定义二分查找,因为bisect模块的函数可能需要特定的条件调整。 另外,用户可能需要不同编程语言的例子,比如C++或Python。根据引用1中的C++代码,可以调整比较符号,将>改为>=吗?不过通常LIS的贪心算法是维护一个递增序列,对于LNDS,可能需要允许相等的元素,所以在查找插入位置时,使用upper_bound而不是lower_bound,或者调整比较函数。 需要验证这种方法的正确性。例如,对于输入序列[3,4,4,5,2,3,7],正确的最长不下降子序列长度应该是5(比如3,4,4,5,7)。用贪心+二分的方法,tails数组的更新逻辑是否正确。另外,动态规划方法虽然时间复杂度高,但对于小规模数据可能更直观。用户可能希望两种方法都了解,所以需要给出两种实现,并说明优缺点。 最后,确保代码示例正确,并解释关键步骤,比如在贪心算法中如何维护tails数组,以及二分查找的作用。同时,引用中的内容需要正确标注,比如引用3提到的尾部比较优化,对应贪心方法。</think>### 最长不下降子序列算法实现 #### 算法核心思路 最长不下降子序列(Longest Non-Decreasing Subsequence,LNDS)要求子序列中元素**非严格递增**(允许相邻元素相等)。其核心解法与最长上升子序列(LIS)类似,但需调整比较条件[^3]。 #### 两种实现方法 1. **动态规划法**(时间复杂度 $O(n^2)$) - 定义数组 $dp[i]$ 表示以第 $i$ 个元素结尾的最长不下降子序列长度。 - 状态转移方程: $$dp[i] = \max_{\substack{0 \leq j < i \\ a[j] \leq a[i]}} (dp[j] + 1)$$ 2. **贪心+二分优化法**(时间复杂度 $O(n \log n)$) - 维护一个数组 `tails`,记录所有长度子序列的最小末尾值。 - 遍历元素时,若当前值 $\geq$ `tails` 末尾,直接追加;否则通过二分查找替换第一个严格大于它的位置。 --- #### 代码实现 ##### Python(贪心+二分优化) ```python import bisect def length_of_lnds(nums): tails = [] for num in nums: idx = bisect.bisect_right(tails, num) # 允许相等时插入右侧 if idx == len(tails): tails.append(num) else: tails[idx] = num return len(tails) # 示例 nums = [3, 4, 4, 5, 2, 3, 7] print(length_of_lnds(nums)) # 输出5(序列3,4,4,5,7) ``` ##### C++(动态规划) ```cpp #include <vector> #include <algorithm> using namespace std; int length_of_lnds(vector<int>& nums) { int n = nums.size(); vector<int> dp(n, 1); for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (nums[j] <= nums[i]) { // 允许相等 dp[i] = max(dp[i], dp[j] + 1); } } } return *max_element(dp.begin(), dp.end()); } ``` --- #### 关键点对比 | 方法 | 时间复杂度 | 适用场景 | |--------------|------------|------------------------| | 动态规划 | $O(n^2)$ | 小规模数据,代码直观 | | 贪心+二分 | $O(n \log n)$ | 大规模数据,效率优先 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值