#546. 最长不下降序列(LIS)

给定一个整数序列,文章描述了如何找出其中最长的不下降子序列,提供了暴力解法和动态规划解决方案,并给出了样例输入和输出。动态规划方法能更有效地找到答案。

Background

Special for beginners, ^_^

Description

设有由 nn 个不相同的整数组成的数列,记为:b_1,b_2,\dots,b_nb1,b2,…,bn 且 b_i\neq b_j (i\neq j)bi=bj(i=j),若存在 i_1<i_2<i_3<\dots < i_ei1<i2<i3<⋯<ie 且有 b_{i_1}<b_{i_2}<\dots <b_{i_e}bi1<bi2<⋯<bie 则称为长度为 ee 的不下降序列。

当原数列出之后,求出最长的不下降序列。

例如 13,7,9,16,38,24,37,18,44,19,21,22,63,15。

例中 13,16,18,19,21,22,63 就是一个长度为 7 的不下降序列,同时也有 7,9,16,18,19,21,22,63 长度为 8 的不下降序列。

Format

Input

输入一行若干个整数,个数小于 100,取值小于 50000。

Output

输出两行。

第一行输出一个整数 e。

第二行输出 e 个整数,为最长不下降序列。

Samples

输入数据 1

300 250 275 252 200 138 245

Copy

输出数据 1

2
250 275 

Copy

Limitation

1s, 1024KiB for each test case.

/*
这是暴戾做法,时间较慢
#include <bits/stdc++.h>
using namespace std;

const int N=107;

int b[N][5];

int main(){
    int n=0;
    int l,k;
    while(cin>>b[++n][1]){
        b[n][2]=1;
        b[n][3]=0;
    }
    n--;
    for(int i=1;i<=n;i++){
        cin>>b[i][1];
        b[i][2]=1;
        b[i][3]=0;
    }
    for(int i=n-1;i>=1;i--){
        l=0;
        k=0;
        for(int j=i+1;j<=n;j++){
            if((b[j][1]>b[i][1])&&(b[j][2]>l)){
                l=b[j][2];
                k=j;
            }
            if(l>0){
                b[i][2]=l+1;
                b[i][3]=k;
            }
        }
    }
    k=1;
    for(int j=1;j<=n;j++){
        if(b[j][2]>b[k][2])k=j;
    }
    cout<<b[k][2]<<endl;
    while(k){
        cout<<b[k][1]<<" ";
        k=b[k][3];
    }
    return 0;
}
下面是动态规划,比较快
*/
#include<iostream>
using namespace std;

const int N=1007;

int dp[N];

int a[N];

int num[N];

int h,d,c;

int w=1;

int MXN;

void print(int a){
    if(!dp[a])return;
    print(dp[a]);
    cout<<num[a]<<" ";
}

int main() {
    while(cin>>num[++h]);
    for(int i=1;i<=h;++i){
        a[i]=1;
        MXN=0;
        for(int j=1;j<i;++j){
            if(a[j]>MXN&&num[j]<num[i]) {
                MXN=a[j];
                a[i]=a[j]+1;
                c=j;
            }
        }
        dp[i]=c;
        if(a[i]>d){
            d=a[i];
            w=i;
        }
    }
    cout<<d<<endl;
    print(w);
    return 0;
}
<think>好的,我现在需要帮助用户解决关于最长下降子序列算法和实现的问题。首先,我需要确认用户的具体需求是什么。用户提到了“最长下降子序列”,并希望了解算法及其实现。根据提供的引用内容,用户可能已经了解了一些基础概念,但需要更详细的解释和代码示例。 首先,我需要回顾最长下降子序列(Longest Non-Decreasing Subsequence,LNDS)的基本定义。根据引用[1],LNDS是在一个序列中找到最长子序列,允许元素相等或递增。这与最长上升子序列LIS同,LIS要求严格递增,而LNDS允许相等的情况。这一点很重要,因为在实现时处理条件会有所同,用户可能在区分这两者时有困惑,引用[2]也提到要混淆这两者,所以需要明确说明。 接下来,用户需要算法和实现。传统的动态规划方法是O()时间复杂度,这在数据量大时可能够高效。用户提供的引用[3]和[4]提到了O(n log n)的优化算法,使用二分查找来维护一个有序数组,从而降低复杂度。因此,我应该重点介绍这两种方法,并比较它们的优缺点。 对于动态规划方法,每个元素dp[i]表示以第i个元素结尾的最长下降子序列长度。需要两层循环,外层遍历每个元素,内层遍历之前的元素,找到可以构成下降序列的元素并更新长度。这种方法简单直观,但效率较低,适合小规模数据。 然后是O(n log n)的优化算法,这里需要维护一个数组d,其中d[k]表示长度为k+1的子序列的最小末尾元素。遍历每个元素时,使用二分查找找到在d数组中第一个大于当前元素的位置,并进行替换。这里需要注意,当元素可以相等时,二分查找的条件是找到第一个大于的元素,如果是LIS则需要找到大于等于的位置。引用[4]提到d[k]是长度为k的子序列的最小末尾元素,所以在处理时,遇到相等的元素可以替换,从而允许后续更小的元素延长子序列的长度。 在代码实现方面,用户可能需要Python的示例。需要确保动态规划和优化算法的代码正确,特别是二分查找的部分。例如,在优化算法中,bisect_right用于处理下降的情况,因为它允许相等的元素插入到右侧,保持序列的非递减性。而bisect_left则可能用于LIS的情况。需要测试同的情况,如引用[2]提到的样例“1 2 3 2 3 2”,确保算法正确计算最长下降子序列的长度。 此外,用户可能关心应用场景和优化方法。根据引用[3],HDU 1025题目需要处理较大的数据,因此O(n log n)算法更为适用。应用场景可能包括数据流分析、生物信息学中的序列比对、股票价格分析等,这些需要高效处理长序列的情况。 最后,生成相关问题时要围绕用户可能感兴趣的扩展内容,例如算法的比较、具体实现中的注意事项、实际应用案例等。确保问题能够引导用户进一步深入学习,并帮助他们理解如何应用这些算法解决实际问题。 总结来说,我需要分步骤解释两种算法,提供代码示例,并指出实现中的关键点,如二分查找的选择和条件判断。同时,强调正确区分最长下降子序列最长上升子序列的重要性,避免用户混淆。最后,通过相关问题引导用户深入探索算法的细节和应用。</think>### 最长下降子序列算法实现 最长下降子序列(Longest Non-Decreasing Subsequence, LNDS)的经典算法分为两种实现方式:$O(n^2)$动态规划和$O(n \log n)$优化算法。以下是详细说明: --- #### 一、动态规划法($O(n^2)$) **核心思路**: 定义$dp[i]$表示以第$i$个元素结尾的最长下降子序列长度。状态转移方程为: $$ dp[i] = \max(dp[j] + 1) \quad \text{当} \ a[j] \leq a[i] \ \text{且} \ j < i $$ 最终结果为$\max(dp[0...n-1])$。 **Python实现**: ```python def length_of_LNDS(nums): n = len(nums) dp = [1] * n for i in range(n): for j in range(i): if nums[j] <= nums[i]: # 允许相等,区别于严格上升 dp[i] = max(dp[i], dp[j] + 1) return max(dp) if n > 0 else 0 ``` --- #### 二、优化算法($O(n \log n)$) **核心思路**: 维护一个数组`d`,其中`d[k]`表示长度为`k+1`的最长下降子序列的最小末尾元素[^4]。通过二分查找确定当前元素的插入位置。 **Python实现**: ```python import bisect def length_of_LNDS_optimized(nums): d = [] for num in nums: idx = bisect.bisect_right(d, num) # 允许相等,用bisect_right if idx == len(d): d.append(num) else: d[idx] = num return len(d) ``` **关键点**: 1. 使用`bisect_right`而非`bisect_left`,确保相等元素可重复出现[^2] 2. 例如输入`[1,2,3,2,3,2]`时,正确输出长度为4(序列`1,2,2,3`) --- #### 三、两种算法对比 | 方法 | 时间复杂度 | 适用场景 | |-------------|------------|--------------------| | 动态规划 | $O(n^2)$ | 数据规模较小(n≤1e3)| | 二分+贪心 | $O(n \log n)$ | 大规模数据(n≤1e6)[^3]| --- ### 测试样例验证 输入:`nums = [1,2,3,2,3,2]` - 动态规划输出:4 - 优化算法输出:4 正确序列为`[1,2,2,3]`或`[1,2,3,3]`,验证算法正确性 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值