Make Array Strictly Increasing 使数组严格递增
问题描述:
给你两个整数数组 arr1 和 arr2,返回使 arr1 严格递增所需要的最小操作数(可能为 0)。
每一步操作中,你可以分别从 arr1 和 arr2 中各选出一个索引,分别为 i 和 j,0 <= i < arr1.length 和 0 <= j < arr2.length,然后进行赋值运算 arr1[i] = arr2[j]。
如果无法让 arr1 严格递增,请返回 -1。
arr1.length,arr2.length 范围[1,2000]
arr1[i], arr2[i] 范围[0,10^9]
分析
要计算将arr1数组最终达到严格递增操作的最少此时,而一次操作,就是从arr2中选一个元素赋值到arr1中,因为要求最终是严格递增的,所以不会允许arr2中的一个元素使用2次以上。
因为要求操作最少,最理想的情况下就是0操作,即原始的数组就是满足严格递增的。
对于长度n的数组来说,对具体的i,arr[i]进行操作是一个问题,将arr[i] 更新成为x,y,z,又是一个问题。
每一个a[i]都有可能更新,但是更新的策略就很关键。
如果是从左到右,出现a[i]>=a[i+1],具体更新哪一个,如果可以更新a[i]变小,或者a[i+1]变大,都可以满足。正向的思路不流畅,无法找到共性。
如果从右向左,对于a[i]来说,可以选择更新成为a2中最大的,或者不更新,那么对于a[i-1],就有限制,限制来源于此时的a[i],记此时的a[i]为 tar。
情况 1
如果a[i-1]>=tar,说明要满足题意a[i-1]就必须更新为a’[i-1]<tar,而且要尽可能的靠近tar,同时在a2中寻找是否存在这样的元素,如果存在那么就可以更新,否则这个序列就不可能存在。
情况 2
如果a[i-1]<tar,说明在不修改a[i-1]的情况下,就可以满足题意,可以去看0~i-2。
当然也可以将a[i-1],修改为小于tar的最大元素,
所以可以发现定义问题就是 对a[i] 是否选择修改,而且要满足a[i]<tar的情况下的最小操作数。
用dfs(int ind,int tar),ind表示当0~ind为严格递增,&&a[ind]<tar时 的最小操作数。
不修改a[ind] 【当然必须满足a[ind]<tar】,int res1 =dfs(ind-1,a[ind])
修改a[ind],
可以找到最大的 a2[x] <tar,int res2 = dfs(ind-1,a2[x])+1
找不到满足条件的a2[x]<tar,int res2 = INTMAX.
所以当无法找到合适的序列时,dfs应该返回一个较大的值INTMAX。
那么 对于 dfs()来说, 最终返回的应该是min(res1,res2);
递归的边界就是 当ind<0时,返回0.
在上面递归理论上也可以做出从左到右的递推.
但是如果按照递归的思路,f[i][j],j作为第i个位置的取值维度就会比较分散。
就需要做一些调整,定义f[i][j],表示前i个元素,恰好完成j次替换时达到有序序列时的尾部元素可以取到的最小值。
用极大值INTMAX初始化f,最终的结果就是遍历f[n][0~~j],取j的最小值。
因为替换也是有上限的,上边界就是min(a1,a2),同时下边界就是0.如果存在这样的序列,j一定是属于[0,min(a1,a2)].
从左到右的思路,对于a1[i],也分2个情况
1 保持不变,如果a1[i-1]<a1[i],这个选择是允许的,否则这个序列就不可能存在。
2 修改a1[i],修改后的tar值范围(a1[i-1],a1[i+1]),为了保证后续的存在,tar应该从a2中选择属于这个范围内的最小元素。
即f[i][j] = min(a1[i]), && a1[i]>f[i-1][j],//保留
f[i][j] = min(a2[k]), && a2[k]>f[i-1][j-1],//修改
代码
class Solution {
static final int INF = 0x3f3f3f3f;
public int makeArrayIncreasing(int[] arr1, int[] arr2) {
Arrays.sort(arr2);
List<Integer> list = new ArrayList<Integer>();
int prev = -1;
for (int num : arr2) {
if (num != prev) {
list.add(num);
prev = num;
}
}
int n = arr1.length;
int m = list.size();
int[][] dp = new int[n + 1][Math.min(m, n) + 1];
for (int i = 0; i <= n; i++) {
Arrays.fill(dp[i], INF);
}
dp[0][0] = -1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= Math.min(i, m); j++) {
/* 如果当前元素大于序列的最后一个元素 */
if (arr1[i - 1] > dp[i - 1][j]) {
dp[i][j] = arr1[i - 1];
}
if (j > 0 && dp[i - 1][j - 1] != INF) {
/* 查找严格大于 dp[i - 1][j - 1] 的最小元素 */
int idx = binarySearch(list, j - 1, dp[i - 1][j - 1]);
if (idx != list.size()) {
dp[i][j] = Math.min(dp[i][j], list.get(idx));
}
}
if (i == n && dp[i][j] != INF) {
return j;
}
}
}
return -1;
}
public int binarySearch(List<Integer> list, int low, int target) {
int high = list.size();
while (low < high) {
int mid = low + (high - low) / 2;
if (list.get(mid) > target) {
high = mid;
} else {
low = mid + 1;
}
}
return low;
}
}
时间复杂度 O(
N
∗
m
i
n
(
M
,
N
)
∗
L
o
g
M
N*min(M,N)*LogM
N∗min(M,N)∗LogM)
空间复杂度: O(
N
∗
m
i
n
(
M
,
N
)
N*min(M,N)
N∗min(M,N))
Tag
Array
Binary
Dynamic Programming