【动态规划-最长递增子序列(LIS)】【hard】力扣1671. 得到山形数组的最少删除次数

我们定义 arr 是 山形数组 当且仅当它满足:

arr.length >= 3
存在某个下标 i (从 0 开始) 满足 0 < i < arr.length - 1 且:
arr[0] < arr[1] < … < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > … > arr[arr.length - 1]
给你整数数组 nums​ ,请你返回将 nums 变成 山形状数组 的​ 最少 删除次数。

示例 1:
输入:nums = [1,3,1]
输出:0
解释:数组本身就是山形数组,所以我们不需要删除任何元素。

示例 2:
输入:nums = [2,1,1,5,6,2,3,1]
输出:3
解释:一种方法是将下标为 0,1 和 5 的元素删除,剩余元素为 [1,5,6,3,1] ,是山形数组。

在这里插入图片描述

二分查找

class Solution {
public:
    int minimumMountainRemovals(vector<int>& nums) {
        int n = nums.size();

        vector<int> lis(n, 1), lds(n, 1);

        vector<int> inc; 

        for (int i = 0; i < n; ++i) {
            auto it = lower_bound(inc.begin(), inc.end(), nums[i]);
            if (it == inc.end()) {
                inc.push_back(nums[i]);
            } else {
                *it = nums[i];
            }
            lis[i] = inc.size();
        }

        vector<int> dec; 

        // 计算从右到左的最长递减子序列 (LDS) 长度,使用二分查找
        for (int i = n - 1; i >= 0; --i) {
            auto it = lower_bound(dec.begin(), dec.end(), nums[i]);
            if (it == dec.end()) {
                dec.push_back(nums[i]);
            } else {
                *it = nums[i];
            }
            lds[i] = dec.size();
        }


        int minRemovals = n;
        for (int i = 1; i < n - 1; ++i) {
            if (lis[i] > 1 && lds[i] > 1) {
                // 山峰的长度是 lis[i] + lds[i] - 1
                minRemovals = min(minRemovals, n - (lis[i] + lds[i] - 1));
            }
        }

        return minRemovals;
    }
};

时间复杂度:O(nlogn),其中 n 是数组 nums 的长度。

空间复杂度:O(n)。

这道题的思路就是构建一个最长递增子序列LIS和最长递减子序列LDS。
开始,我们定义vector<int> lis(n, 1), lds(n, 1);来储存以nums[i]结尾的最长递增/递减子序列长度。

我们定义一个迭代器it,指向通过二分查找找到的inc数组中的大于等于nums[i]的元素。
需要注意是
由于 upper_bound 查找的是严格大于当前值的元素,它确保了序列是非严格递增的,即允许相同的元素出现在序列中。例如,如果当前序列为 1, 2, 3,新加入的元素也是 3,upper_bound 会找到序列末尾并在适当位置插入新的 3,从而维持非严格递增的性质。

而lower_bound查找的是大于等于nums[i]的元素,所以nums[i]如果有与inc中相等的元素,会替换而不是推入,这保证了最长递增子序列是严格递增的。

我们还定义了lis和lds来储存不同nums[i]结尾的最长递增。递减子序列的长度。我们遍历每个元素结尾的最长递增子序列和最长递减子序列的长度。·然后通过minRemovals来记录山峰数组的最少删除次数。

最后返回minRemovals。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值