Make Array Strictly Increasing


Problem

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

In one operation, you can choose two indices and and do the assignment .

If there is no way to make strictly increasing, return

Example

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].
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].
Input: arr1 = [1,5,3,6,7], arr2 = [1,6,3,3]
Output: -1
Explanation: You can't make arr1 strictly increasing.

Constraints

Submit your solution at here

Solution

Intuition

Let's say we traverse arr1 and encounter a abnormal positioning. How do we pick number from arr2 to adjust that?

  • We don't want to pick large number early because that's only put us into disavantage situation
  • For each decision, we want to pick smallest number that do not break the arr1's constraint (strictly increasing)
  • To do so we must sort arr2 and do binary search

Approach

  • DFS and find the lowest cost. That's the first thing come to my mind but sadly it's TLE
  • DP, let is the minimum possible of if we only allowed to pick at most items from

Complexity

  • Time complexity:
    • With DFS approach:
  • Space complexity:
    • Can be further optimized to

Code

DFS (TLE)

use std::cmp::min;
impl Solution {
    pub fn make_array_increasing(mut arr1: Vec<i32>, mut arr2: Vec<i32>) -> i32 {
        let n = arr1.len();
        arr2.sort();
        let mut ret = n+1;
        let mut q = Vec::new();
        q.push((0, arr1[0], 1));
        if arr2[0] < arr1[0] {
            q.push((1, arr2[0], 1));
        }
        while let Some((cost, top, i)) = q.pop() {
            if i == n {
                ret = min(ret, cost);
                continue;
            }
            let mut free_option = i32::MAX;
            if arr1[i] > top {
                q.push((cost, arr1[i], i+1));
                free_option = min(free_option, arr1[i]);
            }
            let j = arr2.partition_point(|&x| x <= top);
            if j >= arr2.len() || arr2[j] > free_option {
                continue;
            }
            q.push((cost+1, arr2[j], i+1));
        }
        if ret > n {
            return -1;
        }
        ret as i32
    }
}

DP (AC)

use std::cmp::min;
impl Solution {
    pub fn make_array_increasing(arr1: Vec<i32>, mut arr2: Vec<i32>) -> i32 {
        let n = arr1.len();
        arr2.sort();
        arr2.dedup();
        let m = arr2.len();
        // f[i][j] = can take at most i item from arr2, what is the minimum of arr1[j]
        // f[0] = cannot take anything from arr2, so f[0] = arr1
        let mut f = vec![vec![i32::MAX;n];m+1];
        for j in 0..n {
            if j == 0 || arr1[j] > f[0][j-1] {
                 f[0][j] = arr1[j]
            } else {
                break
            }
        }
        for i in 1..=m {
            f[i][0] = min(arr1[0], arr2[0]);
            for j in 1..n {
                let prev = f[i-1][j-1];
                let k = arr2.partition_point(|&x| x <= prev);
                if k < m {
                    f[i][j] = min(f[i][j], arr2[k]);
                }
                let prev = min(prev, f[i][j-1]);
                if arr1[j] > prev {
                    f[i][j] = min(f[i][j], arr1[j]);
                }
            }
        }
        //println!("{f:?}");
        f.iter()
            .position(|a| a[n-1] != i32::MAX)
            .map_or(-1, |x| x as i32)
    }
}