【leetcode】使数组严格递增 (dp+二分)

利用动态规划和二分查找的方法解决LeetCode上的一个问题,要求找到使数组arr1严格递增所需的最小操作数。详细介绍了状态转移方程和初始条件,并强调了问题的难点在于从i到i+1的状态转移而非i-1到i。
摘要由CSDN通过智能技术生成

给你两个整数数组 arr1 和 arr2,返回使 arr1 严格递增所需要的最小「操作」数(可能为 0)。

每一步「操作」中,你可以分别从 arr1 和 arr2 中各选出一个索引,分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length,然后进行赋值运算 arr1[i] = arr2[j]。

如果无法让 arr1 严格递增,请返回 -1。

示例 1:

输入:arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]
输出:1
解释:用 2 来替换 5,之后 arr1 = [1, 2, 3, 6, 7]。
示例 2:

输入:arr1 = [1,5,3,6,7], arr2 = [4,3,1]
输出:2
解释:用 3 来替换 5,然后用 4 来替换 3,得到 arr1 = [1, 3, 4, 6, 7]。
示例 3:

输入:arr1 = [1,5,3,6,7], arr2 = [1,6,3,3]
输出:-1
解释:无法使 arr1 严格递增。

提示:

1 <= arr1.length, arr2.length <= 2000
0 <= arr1[i], arr2[i] <= 10^9

链接:https://leetcode-cn.com/problems/make-array-strictly-increasing

思路分析:

对于这道题来说,需要从 i 推到 i+1.
状态很明显了:
dp[i][j] 表示在第 i 个位置以 arr2[j] 或 arr1[i] 结尾的最小操作数。
这里当 j == m 时表示以 arr1[i] 结尾,也就是不换。
我们可以用tmp表示当前状态结尾的数。
如果arr1[i+1] > tmp,i+1位置可以选择不换。对于 dp[i][j] 可以推出 dp[i+1][m]. 即dp[i+1][m] = dp[i][j].
选择i+1位置选择换,那么现在已知当前结尾的值为tmp,必须得从arr2找比tmp大的元素换。当然这个替换的元素越小越好。因此可以想到用二分。
令 idx 为 upper_bound (arr2, tmp) 的索引。
对于当前位置的每个tmp,如果idx存在,就可以更新 i+1 位置。
dp[i+1][idx] = dp[i][j].
上面两种状态转移方程都是取最小。

接下来考虑初始条件:
我们可以初始化dp为-1,表示该状态不存在严格递增。
很容易可以分析出 dp[0][m] = 0, dp[0][j] = 1.

这一道题就写完了。
需要注意的是,我们必须写成 i -> i+1 而不是 i-1 -> i.
如果按照后者的写法,我们需要获取 i-1 状态的结尾值才能更新当前状态。这样很不方便。
而如果按照前者的写法,我们可以获取当前状态的结尾值更新下一个状态。这也是这道题的难点之一。
另外,可以对arr2做一个排序+去重的优化。

class Solution {
   
public:
    int makeArrayIncreasing(vector<int>& arr1, vector<int>& arr2) {
   
        sort(arr2.begin(),
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值