这题还是相当难的,但是给我的启发也很多。
首先先说解法:
从LIS这道题目而来。
LIS的dp方程是:对于j < i,if (nums[j] < nums[i]) , dp[i] = max(dp[i], 1 + dp[j])。
这道题是这样的:
对于j < i, if (nums[j] < nums[i]),我们尝试把j~i之间的所有数字都换成arr2里面的。如果可以的话,设change是需要换的个数,dp[i] = min(dp[i], change+dp[j]).
可能有疑问:为什么要把所有的都换成arr2里面的呢?先看代码,后面解释:
class Solution {
public:
int makeArrayIncreasing(vector& arr1, vector& arr2) {
int sz = arr1.size();
sort(arr2.begin(), arr2.end());
vector dup{arr2[0]};
for (int i = 1; i < arr2.size(); ++i)
if (arr2[i] != arr2[i-1])
dup.push_back(arr2[i]);
arr2 = std::move(dup);
arr1.insert(arr1.begin(), INT_MIN);//因为可能要从第一个开始换,所以最前面加一个
arr1.push_back(INT_MAX);//最后面是最终情况
vector dp(sz+2, INT_MAX);//以arr1[i]结尾的最多要换多少次
dp[0] = 0;
for (int i = 1; i < sz+2; ++i) {
for (int j = 0; j < i; ++j) {
if (arr1[j] < arr1[i] && dp[j] != INT_MAX) {
int change = fun(arr1, arr2, j, i);
if (change >= 0)
dp[i] = min(dp[i], dp[j] + change);
}
}
}
return dp[sz+1] == INT_MAX ? -1 : dp[sz+1];
}
private:
int fun(vector& arr1, vector& arr2, int beg, int end) {
if (beg+1 == end)
return 0;
int idx = binarySearch(arr2, arr1[beg]);
int requireNum = end-beg-1;
if (arr2.size()-idx-1 >= requireNum && arr2[idx+requireNum] < arr1[end])
return requireNum;
else
return -1;
}
int binarySearch(vector& nums, int target) {
int lo = 0, hi = nums.size()-1;
int mid;
while (lo <= hi) {
mid = lo + (hi - lo) / 2;
if (target < nums[mid])
hi = mid-1;
else if (target > nums[mid])
lo = mid+1;
else
return mid;
}
return hi;
}
};
对上面问题的解释,直接上我在评论区的留言吧:
I want to say something confused me at first: why we try to replace all elements in arr1[j+1,i-1] in the check function? If some elements can be used between [j+1, i-1], why we should replace them?
Here is my thought:
we says “some elements can be used” actually means that “there is an index k (i < k < j), arr1[j] < arr1[k] < arr1[i]”,and we don’t want to replace it. But actually even though arr1[j] < arr1[k] < arr1[i], some element arr1[k] must be replaced. For example, arr1=[1, 5, 99, 100, 100]. 99 must be replaced to get the final result. But in other cases, the element arr1[k] can be kept, like arr1 = [1, 5, 10, 100, 100], 10 can be kept. That’s OK, because for (int j = 0; j < i; j++) j will iterate on number 10 and we will get the right answer there.
接下来谈谈这道题目对我的启发:
(面试不一定会问这么难的,但是有了下面的思路面对简单一些的会好想很多)。
(即使问了这么难的,给出思路就很好了,所以思考这类问题还是有意义的)
1.尝试和熟悉的一些经典问题去靠拢。比如这题可以思考LIS。这应该是这道问题最重要的一步。
2.另外,难问题还是由小问题组成的,比如这道题的check函数这么完成算是一个了。