[LeetCode] 1187. Make Array Strictly Increasing 使數組嚴格遞增



Given two integer arrays arr1 and arr2, return the minimum number of operations (possibly zero) needed to make arr1 strictly increasing.

In one operation, you can choose two indices 0 <= i < arr1.length and 0 <= j < arr2.length and do the assignment arr1[i] = arr2[j].

If there is no way to make arr1 strictly increasing, return -1.

Example 1:

Input: arr1 = [1,5,3,6,7], arr2 = [1,3,2,4]
Output: 1
Explanation: Replace `5` with `2`, then `arr1 = [1, 2, 3, 6, 7]`.

Example 2:

Input: arr1 = [1,5,3,6,7], arr2 = [4,3,1]
Output: 2
Explanation: Replace `5` with `3` and then replace `3` with `4`. `arr1 = [1, 3, 4, 6, 7]`.

Example 3:

Input: arr1 = [1,5,3,6,7], arr2 = [1,6,3,3]
Output: -1
Explanation: You can't make `arr1` strictly increasing.

Constraints:

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

這道題給了兩個數組 arr1 和 arr2,說是可以用 arr2 中的數字來替換 arr1 中的數字,問最少需要替換多少次才能使 arr1 中的數字嚴格有序。所謂嚴格有序,就是必須是數字必須從小到大,而且不能有相等的數字出現。而且 arr2 中的每個數字只能使用一次,不過這點說不說都無所謂,因為要求嚴格遞增,用相同的數字替換兩次肯定也不符合題意。再來考慮,都是什么情況下需要替換數字呢,可以參考例子中的情況,當后面的數字小於相當的數字時,就要替換,那究竟是替換當前的數字為小一些的數字,還是替換后面的數字為大一些的數字呢?都有可能,需要考慮所有的情況,這樣一來,整個問題就變的相當復雜了,基本上貪婪算法就不太可能了,這也算能對的其 Hard 的身價了。這里若是要替換后面的數字為較大的數字,那么就需要在 arr2 中找到比當前數字大的數字,為了讓整個數組更容易的遞增,那么這個較大數應該盡量越小越好,所以就是要找到第一個比當前數字大的數。為了更容易的在 arr2 中查找,而不是每次都遍歷整個數組,需要給 arr2 排個序,然后用二分搜索來查找更高效一些,這里也可以將 arr2 放到一個 TreeSet 中,利用其自動排序的特點,之后再進行二分搜索就行了。這道題的正確解法是用動態規划 Dynamic Programming,這里的 dp 表達式比較難想,一般來說,dp 值都是定義為題目中要求的值,而這道題是個例外,這里的 dp[i][j] 表示對於數組中的前j個數字組成的子數組中,需要替換i次可以使得其變為嚴格遞增,且第j個數字的最小值為 dp[i][j]。這里的 dp 值不是定義為替換次數,而是第j個數字的最小值(可能是替換后的值),因為要保證數組嚴格遞增,最后一個數字的大小很重要,這是個關鍵信息,而這個數字的大小跟數組坐標之間沒有啥必然聯系,所以這個信息不太好放到 dp 數組的坐標中,而所求的替換次數跟數組長度是相關的,因為其不可能超過數組的總長度,最差的情況也就是將整個 arr1 數組都替換了(當然還需要考慮 arr2 的長度)。

接下來就來考慮狀態轉移方程怎么寫,由於這里的j表示前j個數字,那么第j個數字實際上是 arr1[j-1],若第j個數字大於 dp[i][j-1],這里表示對於前 j-1 個數字,替換i次可以使得其嚴格遞增,且第 j-1 個數字為 dp[i][j-1],這樣的話就不需要額外的替換操作,還是嚴格遞增增的,則 dp[i][j] 可以賦值為 arr1[j-1]。若此時i大於0,說明之前已經進行過替換操作,則上一個操作狀態是 dp[i-1][j-1],當前操作是從 arr2 中選一個數字替換 arr1 的第j個數字,這里就要在 arr2 中選擇第一個大於 dp[i-1][j-1] 的數字,若存在的話,就用這個數字來更新 dp[i][j] 的值。若某個時刻j等於n了,說明已經到 arr1 的末尾了,若此時 dp[i][j] 不等於 INT_MAX(初始值),說明是可以將整個 arr1 替換成嚴格遞增的數組的,替換次數就是i,直接返回即可。最終循環退出了,返回 -1,參見代碼如下:


class Solution {
public:
    int makeArrayIncreasing(vector<int>& arr1, vector<int>& arr2) {
        int n = arr1.size();
        if (n == 1) return 0;
        set<int> st(arr2.begin(), arr2.end());
        vector<vector<int>> dp(n + 1, vector<int>(n + 1, INT_MAX));
        dp[0][0] = INT_MIN;
        for (int j = 1; j <= n; ++j) {
            for (int i = 0; i <= j; ++i) {
                if (arr1[j - 1] > dp[i][j - 1]) {
                    dp[i][j] = arr1[j - 1];
                }
                if (i > 0) {
                    auto it = st.upper_bound(dp[i - 1][j - 1]);
                    if (it != st.end()) dp[i][j] = min(dp[i][j], *it);
                }  
                if (j == n && dp[i][j] != INT_MAX) return i;
            }
        }
        return -1;
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1187


參考資料:

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

https://leetcode.com/problems/make-array-strictly-increasing/discuss/377403/Python-DP-solution-with-explanation.

https://leetcode.com/problems/make-array-strictly-increasing/discuss/377680/Simple-Java-DP-Solution-%2B-TreeSet-with-Explanation-beats-100


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM