LeetCode 1671. 得到山形数组的最少删除次数(最长上升子序DP nlogn)



197 / 1891,前10.4%

435 / 6154,前7.07%

前三题如下:

LeetCode 5557. 最大重复子字符串
LeetCode 5558. 合并两个链表
LeetCode 5560. 设计前中后队列(deque)

1. 题目

我们定义 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
解释:一种方法是将下标为 015 的元素删除,剩余元素为 [1,5,6,3,1] ,是山形数组。

示例 3:
输入:nums = [4,3,2,1,1,2,3,1]
输出:4

示例 4:
输入:nums = [1,2,3,4,4,3,2,1]
输出:1
 
提示:
3 <= nums.length <= 1000
1 <= nums[i] <= 10^9
题目保证 nums 删除一些元素后一定能得到山形数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-number-of-removals-to-make-mountain-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 解题

可以参考:

动态规划应用–最长递增子序列 LeetCode 300

  • 计算每个位置处的最长上升子序长度
  • 正反双向计算2次
  • 然后遍历每个位置,计算 m i n ( n − l 1 − l 2 + 1 ) min(n-l1-l2+1) min(nl1l2+1)

2.1 n^2 解法

class Solution {
public:
    int minimumMountainRemovals(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp1(n, 1), dp2(n, 1);
        for(int i = 1; i < n; ++i)
        {
            for(int j = i-1; j >= 0; --j)
            {
                if(nums[j] < nums[i])
                    dp1[i] = max(dp1[i], dp1[j]+1);
            }
        }
        for(int i = n-2; i >= 0; --i)
        {
            for(int j = i+1; j < n; ++j)
            {
                if(nums[j] < nums[i])
                    dp2[i] = max(dp2[i], dp2[j]+1);
            }
        }
        int ans = INT_MAX;
        for(int i = 1; i < n-1; ++i)
        {
            if(dp1[i]>1 && dp2[i]>1)
                ans = min(ans, n-(dp1[i]+dp2[i]-1));
        }
        return ans;
    }
};

444 ms 12.2 MB C++

2.2 nlogn 解法

参考评论区:Zhenghao-Liu

采用了二分查找的方法,直接找到当前数字该插入的位置

直接复制过来Zhenghao-Liu大佬的代码,如下:

const int MAXN=1000;
int l2r[MAXN];
int r2l[MAXN];
const int INF=0x3f3f3f3f;
class Solution {
public:
    int minimumMountainRemovals(vector<int>& nums) {
        int size=nums.size();
        memset(l2r,0x3f,sizeof(l2r));
        memset(r2l,0x3f,sizeof(r2l));
        vector<int> vec;
        vec.reserve(size);
        for (int i=0;i<size;++i)
        {
            int cur=nums.at(i);
            auto pos=lower_bound(vec.begin(),vec.end(),cur);
            if (pos==vec.end())
                vec.push_back(cur);
            else
                *pos=cur;
            l2r[i]=vec.size();
        }
        vec.clear();
        for (int i=size-1;i>=0;--i)
        {
            int cur=nums.at(i);
            auto pos=lower_bound(vec.begin(),vec.end(),cur);
            if (pos==vec.end())
                vec.push_back(cur);
            else
                *pos=cur;
            r2l[i]=vec.size();
        }
        int ans=INF;
        for (int i=1;i<size-1;++i)
        {
            int l=l2r[i];
            int r=r2l[i];
            if (l>1 && r>1)
                ans=min(ans,size-(l+r-1));
        }
        return ans;
    }
};

56 ms 11.9 MB C++


我的CSDN博客地址 https://michael.blog.csdn.net/

长按或扫码关注我的公众号(Michael阿明),一起加油、一起学习进步!
Michael阿明

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael阿明

如果可以,请点赞留言支持我哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值