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/