算法-回溯篇10-非递减子序列

非递减子序列

力扣题目链接

题目描述

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

解题思路

每次回溯传入数组,下标和当前为了维持非递减的最小值;
因为数据范围是-100到100,所以第一次传入-101;
里面使用一个unordered_map,用于记录本层已经使用过的数据,这样可以防止重复序列的出现;
因为答案要求长度至少为2的子集,所以每次大于2都加入到答案中。

题解

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;

    void BackTrack(vector<int> nums, int index, int mi){
        if(path.size() >= 2){
            ans.push_back(path);
        }
        unordered_map<int, bool> um;
        for(int i = index; i < nums.size(); i++){
            if(i != index && um[nums[i]] == true){
                continue;
            }
            if(nums[i] >= mi){
                um[nums[i]] = true;
                path.push_back(nums[i]);
                BackTrack(nums, i + 1, nums[i]);
                path.pop_back();
            }
        }

        return;
    }

    vector<vector<int>> findSubsequences(vector<int>& nums) {
        BackTrack(nums, 0, -101);
        return ans;
    }
};

总结

本体最重要的是如何去重,
这道题目中有一个测试用例{1,2,3,4,5,6,7,8,9,10,1,1,1,1,1}
这里我们会发现,如果使用之前的去重策略,只能应对相同数据在一起的情况,这里多个1分布在不同的位置,不适用。
那么我们需要用一个umordered_map来记录下这一层是否使用过(这里可能比较难理解)。

<think>好的,用户想了解最长递减子序列算法实现和解决方法。首先,我需要回忆相关知识。最长递减子序列(Longest Decreasing Subsequence,LDS)与最长递增子序列(LIS)类似,只是元素的顺序是递减的。所以解决方法应该可以借鉴LIS的算法,稍作调整即可。 用户提供的引用中有提到,引用[2]直接讨论了最长递减子序列的问题,并提到了简单解决方案和优化方案。而引用[3]则详细讲解了LIS的O(n log n)算法,这可能对优化LDS有帮助。 首先,应该先介绍基本解法,即动态规划的O(n²)方法。动态规划的思路是,对于每个元素,检查之前所有比它大的元素,找到最长的子序列长度加一。这一步需要双重循环,时间复杂度较高。 接下来是优化方法。对于LIS,通常使用贪心加二分查找来优化到O(n log n)。同样,LDS可以通过将原数组元素取负数,转化为LIS问题,或者直接调整比较逻辑。例如,维护一个数组d,其中d[i]表示长度为i的递减子序列的最小末尾元素(这里可能需要调整,因为递减的话,可能需要最大的末尾元素)。不过根据引用[3]中的方法,维护末尾元素的最小值,但方向相反的话,可能需要维护最大值,或者调整比较条件。 这里可能需要更仔细的推导。例如,在LIS的O(n log n)算法中,数组d维护的是递增序列的最小末尾,以便后续元素可以更容易接在后面。对于递减的情况,可能需要维护一个递减序列,此时每个新元素需要找到第一个比它小的元素的位置,替换或追加。或者,可以将原数组反转,求LIS,再反转回来,或者将每个元素取负数,再求LIS,这样就能利用现有的LIS算法来得到LDS的结果。这种方法可能更简便。 用户的问题是关于算法实现,所以需要给出具体的步骤和代码示例。例如,先介绍动态规划的方法,然后介绍优化的O(n log n)方法,并给出代码实现。 在代码部分,用户可能需要不同语言的示例,比如Python或Java。根据引用中的例子,用户提到Java中的实现,但用户现在的问题可能希望看到Python的实现,或者通用的伪代码。 同时,需要考虑如何处理元素相等的情况。例如,最长不下降子序列允许相等,而严格递减可能需要不同处理。用户的问题中提到的是最长递减子序列,可能需要明确是否允许相等。根据引用[3],最长不下降子序列允许相等,但这里的问题是最长递减,可能指的是严格递减还是不降。需要明确这一点,或者在算法中说明如何处理。 另外,引用[2]提到了如何打印最长递减子序列,这也是用户可能关心的部分。因此,在回答中可以包括两种方法:求长度和打印序列本身。 总结回答的结构应该是: 1. 问题定义 2. 动态规划解法(O(n²)) 3. 优化解法(O(n log n)) 4. 代码示例(可能包括两种方法的实现) 5. 相关扩展问题 需要确保LaTeX格式正确,如动态规划的状态转移方程使用$$...$$,行内变量用$...$。例如,状态转移方程可以写成: $$ dp[i] = \max(dp[j] + 1) \quad \text{其中} \quad j < i \quad \text{且} \quad a[j] > a[i] $$ 此外,引用中的内容需要正确标注。例如,在介绍动态规划时,可以引用[2]提到的简单解决方案,优化方法可以参考[3]中的nlogn算法思路,但应用到递减的情况。 在代码部分,Python的实现可能更简洁。例如,对于动态规划方法,可以初始化一个dp数组,每个元素初始为1,然后双重循环更新。对于优化方法,维护一个数组d,遍历每个元素,使用bisect模块来找到插入位置,但需要注意递减顺序可能需要调整bisect的比较逻辑。 可能遇到的错误点:在优化算法中,由于是递减序列,需要将bisect的查找方向调整,或者对数组取负数,然后使用bisect的升序处理。例如,将每个元素取负数,然后使用bisect_left找LIS的位置,这样可以间接得到LDS的长度。 最后,生成相关问题需要考虑用户可能的后续疑问,如时间复杂度的比较、如何打印序列、处理有重复元素的情况等。</think>### 最长递减子序列算法实现与解决方案 #### 1. 问题定义 最长递减子序列(Longest Decreasing Subsequence, LDS)指在给定序列中找到一个子序列,使得元素严格递减(或不严格递减),且长度最长。例如序列$[9,4,3,5,2]$的最长递减子序列为$[9,5,2]$(严格递减)或$[9,4,3,2]$(非严格递减)。 #### 2. 动态规划解法($O(n^2)$) **核心思路**: 定义$dp[i]$为以第$i$个元素结尾的最长递减子序列长度。状态转移方程为: $$ dp[i] = \max(dp[j] + 1) \quad \text{其中} \quad j < i \quad \text{且} \quad a[j] > a[i] $$ 初始时所有$dp[i]=1$,最终结果为$\max(dp)$[^2]。 **Python实现**: ```python def lds_dp(arr): n = len(arr) dp = [1] * n for i in range(n): for j in range(i): if arr[j] > arr[i]: # 严格递减条件 dp[i] = max(dp[i], dp[j] + 1) return max(dp) ``` #### 3. 优化解法($O(n \log n)$) **核心思路**: 利用贪心+二分查找维护一个递减序列的“潜在最优结构”。定义数组$d$,其中$d[i]$表示长度为$i+1$的递减子序列的最小末尾值(允许非严格递减时)或最大末尾值(严格递减时)。遍历元素时,通过二分查找找到插入位置并更新$d$[^3]。 **Python实现**: ```python import bisect def lds_nlogn(arr, strict=True): d = [] for num in arr: # 根据条件调整比较逻辑 if strict: idx = bisect.bisect_left(d, -num) # 转换为递增问题(取负数) else: idx = bisect.bisect_right(d, num, key=lambda x: -x) # 非严格递减 if idx == len(d): d.append(-num if strict else num) else: d[idx] = -num if strict else num return len(d) ``` #### 4. 算法对比 | 方法 | 时间复杂度 | 空间复杂度 | 适用场景 | |---------------|------------|------------|------------------------| | 动态规划 | $O(n^2)$ | $O(n)$ | 小规模数据或需输出路径 | | 贪心+二分查找 | $O(n \log n)$ | $O(n)$ | 大规模数据仅需长度 | #### 5. 扩展:如何输出具体序列? 若需输出最长递减子序列的具体元素,需额外记录路径信息。以动态规划为例: ```python def lds_with_path(arr): n = len(arr) dp = [1] * n prev = [-1] * n # 记录前驱索引 max_len, end_idx = 1, 0 for i in range(n): for j in range(i): if arr[j] > arr[i] and dp[j] + 1 > dp[i]: dp[i] = dp[j] + 1 prev[i] = j if dp[i] > max_len: max_len = dp[i] end_idx = i # 回溯路径 path = [] while end_idx != -1: path.append(arr[end_idx]) end_idx = prev[end_idx] return path[::-1] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值