1. 程式人生 > 其它 >[LeetCode] 1187. Make Array Strictly Increasing 使陣列嚴格遞增

[LeetCode] 1187. Make Array Strictly Increasing 使陣列嚴格遞增


Given two integer arraysarr1andarr2, return the minimum number of operations (possibly zero) neededto makearr1strictly increasing.

In one operation, you can choose two indices0 <=i < arr1.lengthand0 <= j < arr2.lengthand do the assignmentarr1[i] = arr2[j].

If there is no way to makearr1strictly 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 題目講解彙總(持續更新中...)