2022-08-06:给定一个数组arr,长度为N,arr中所有的值都在1~K范围上, 你可以删除数字,目的是让arr的最长递增子序列长度小于K。 返回至少删除几个数字能达到目的。 N <= 10^4

Posted 福大大架构师每日一题

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022-08-06:给定一个数组arr,长度为N,arr中所有的值都在1~K范围上, 你可以删除数字,目的是让arr的最长递增子序列长度小于K。 返回至少删除几个数字能达到目的。 N <= 10^4相关的知识,希望对你有一定的参考价值。

2022-08-06:给定一个数组arr,长度为N,arr中所有的值都在1~K范围上,
你可以删除数字,目的是让arr的最长递增子序列长度小于K。
返回至少删除几个数字能达到目的。
N <= 10^4,K <= 10^2。
来自京东。4.2笔试。

答案2022-08-06:

动态规划。
时间复杂度:O(NK)。
额外空间复杂度:O(N
K)。
rust和typescript的代码都有。

代码用rust编写。代码如下:

use rand::Rng;
fn main() 
    let nn: i32 = 15;
    let kk: i32 = 6;
    let test_time: i32 = 10000;
    println!("测试开始");
    for _ in 0..test_time 
        let len = rand::thread_rng().gen_range(0, nn) + 1;
        let k = rand::thread_rng().gen_range(0, kk) + 1;
        let mut arr = random_array(len, k);
        let ans1 = min_remove1(&mut arr, k);
        let ans2 = min_remove2(&mut arr, k);
        if ans1 != ans2 
            println!("ans1 = :?", ans1);
            println!("ans2 = :?", ans2);
            println!("出错了!");
            break;
        
    
    println!("测试结束");


// 暴力方法
// 为了验证
fn min_remove1(arr: &mut Vec<i32>, k: i32) -> i32 
    let mut path0: Vec<i32> = vec![];
    for _ in 0..arr.len() 
        path0.push(0);
    
    return process1(arr, 0, &mut path0, 0, k);


const MAX_VALUE: i32 = 2 << 31 - 1;

fn process1(arr: &mut Vec<i32>, index: i32, path0: &mut Vec<i32>, size: i32, k: i32) -> i32 
    if index == arr.len() as i32 
        return if length_of_lis(path0, size) < k 
            arr.len() as i32 - size
         else 
            MAX_VALUE
        ;
     else 
        let p1 = process1(arr, index + 1, path0, size, k);
        path0[size as usize] = arr[index as usize];
        let p2 = process1(arr, index + 1, path0, size + 1, k);
        return get_min(p1, p2);
    


fn get_min<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T 
    if a < b 
        a
     else 
        b
    


fn get_max<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T 
    if a > b 
        a
     else 
        b
    


fn length_of_lis(arr: &mut Vec<i32>, size: i32) -> i32 
    if size == 0 
        return 0;
    
    let mut ends: Vec<i32> = vec![];
    for _ in 0..size 
        ends.push(0);
    
    ends[0] = arr[0];
    let mut right: i32 = 0;
    let mut l;
    let mut r;
    let mut m;
    let mut max = 1;
    for i in 1..size 
        l = 0;
        r = right;
        while l <= r 
            m = (l + r) / 2;
            if arr[i as usize] > ends[m as usize] 
                l = m + 1;
             else 
                r = m - 1;
            
        
        right = get_max(right, l);
        ends[l as usize] = arr[i as usize];
        max = get_max(max, l + 1);
    
    return max;


// arr[0...index-1]上,选择了一些数字,之前的决定!
// len长度了!len = 3 : 1 2 3
// arr[index....]是能够决定的,之前的,已经不能再决定了
// 返回:让最终保留的数字,凑不足k长度的情况下,至少要删几个!
fn zuo(arr: &mut Vec<i32>, index: i32, len: i32, k: i32) -> i32 
    if len == k 
        return MAX_VALUE;
    
    // 凑的(1...len)还不到(1...k)
    if index == arr.len() as i32 
        return 0;
    
    // 没凑到 < k, 有数字!
    let cur = arr[index as usize];
    // 可能性1:保留
    // 可能性2:删除
    // 1...3 3
    if len >= cur || len + 1 < cur 
        return zuo(arr, index + 1, len, k);
    
    // 1..3  4
    // len + 1  == cur
    // 可能性1:保留
    let p1 = zuo(arr, index + 1, len + 1, k);
    // 可能性2:删除
    let mut p2 = MAX_VALUE;
    let next2 = zuo(arr, index + 1, len, k);
    if next2 != MAX_VALUE 
        p2 = 1 + next2;
    
    return get_min(p1, p2);


// 正式方法
// 时间复杂度O(N*K)
fn min_remove2(arr: &mut Vec<i32>, k: i32) -> i32 
    let n = arr.len() as i32;
    let mut dp: Vec<Vec<i32>> = vec![];
    for i in 0..n 
        dp.push(vec![]);
        for _ in 0..k 
            dp[i as usize].push(0);
        
    
    for i in 0..n 
        for j in 0..k 
            dp[i as usize][j as usize] = -1;
        
    
    return process2(arr, k, 0, 0, &mut dp);


fn process2(arr: &mut Vec<i32>, k: i32, index: i32, range: i32, dp: &mut Vec<Vec<i32>>) -> i32 
    if range == k 
        return MAX_VALUE;
    
    if index == arr.len() as i32 
        return 0;
    
    if dp[index as usize][range as usize] != -1 
        return dp[index as usize][range as usize];
    
    let mut ans: i32;
    if arr[index as usize] == range + 1 
        let mut p1 = process2(arr, k, index + 1, range, dp);
        p1 += if p1 != MAX_VALUE  1  else  0 ;
        let p2 = process2(arr, k, index + 1, range + 1, dp);
        ans = get_min(p1, p2);
     else 
        ans = process2(arr, k, index + 1, range, dp);
    
    dp[index as usize][range as usize] = ans;
    return ans;


// 为了测试
fn random_array(len: i32, k: i32) -> Vec<i32> 
    let mut arr: Vec<i32> = vec![];
    for _ in 0..len 
        arr.push(rand::thread_rng().gen_range(0, k) + 1);
    
    return arr;


执行结果如下:

代码用typescript编写。代码如下:

// 暴力方法
// 为了验证
function minRemove1(arr: number[], k: number): number 
  return process1(arr, 0, new Array(arr.length), 0, k);


var MAX_VALUE: number = 2147483647;
function process1(
  arr: number[],
  index: number,
  path: number[],
  size: number,
  k: number
): number 
  if (index == arr.length) 
    return lengthOfLIS(path, size) < k ? arr.length - size : MAX_VALUE;
   else 
    var p1: number = process1(arr, index + 1, path, size, k);
    path[size] = arr[index];
    var p2: number = process1(arr, index + 1, path, size + 1, k);
    return Math.min(p1, p2);
  


function lengthOfLIS(arr: number[], size: number): number 
  if (size == 0) 
    return 0;
  
  var ends: number[] = new Array(size);

  ends[0] = arr[0];
  var right: number = 0;
  var l: number = 0;
  var r: number = 0;
  var m: number = 0;
  var max: number = 1;
  for (var i: number = 1; i < size; i++) 
    l = 0;
    r = right;
    while (l <= r) 
      m = Math.floor((l + r) / 2);
      if (arr[i] > ends[m]) 
        l = m + 1;
       else 
        r = m - 1;
      
    
    right = Math.max(right, l);
    ends[l] = arr[i];
    max = Math.max(max, l + 1);
  
  return max;


// arr[0...index-1]上,选择了一些数字,之前的决定!
// len长度了!len = 3 : 1 2 3
// arr[index....]是能够决定的,之前的,已经不能再决定了
// 返回:让最终保留的数字,凑不足k长度的情况下,至少要删几个!
function zuo(arr: number[], index: number, len: number, k: number): number 
  if (len == k) 
    return MAX_VALUE;
  
  // 凑的(1...len)还不到(1...k)
  if (index == arr.length) 
    return 0;
  
  // 没凑到 < k, 有数字!
  var cur: number = arr[index];
  // 可能性1:保留
  // 可能性2:删除
  // 1...3 3
  if (len >= cur || len + 1 < cur) 
    return zuo(arr, index + 1, len, k);
  
  // 1..3  4
  // len + 1  == cur
  // 可能性1:保留
  var p1: number = zuo(arr, index + 1, len + 1, k);
  // 可能性2:删除
  var p2: number = MAX_VALUE;
  var next2: number = zuo(arr, index + 1, len, k);
  if (next2 != MAX_VALUE) 
    p2 = 1 + next2;
  
  return Math.min(p1, p2);


// 正式方法
// 时间复杂度O(N*K)
function minRemove2(arr: number[], k: number): number 
  var n: number = arr.length;
  var dp: number[][] = new Array(n);
  for (var i: number = 0; i < n; i++) 
    dp[i] = new Array(k);
  
  for (var i: number = 0; i < n; i++) 
    for (var j: number = 0; j < k; j++) 
      dp[i][j] = -1;
    
  
  return process2(arr, k, 0, 2022-08-22:给定一个数组arr,长度为n,最多可以删除一个连续子数组, 求剩下的数组,严格连续递增的子数组最大长度。 n <= 10^6。 来自字节。5.6笔试。

2021-08-03:完美洗牌问题。给定一个长度为偶数的数组arr,假设长度为N*2,左部分:arr[L1……Ln],右部分: arr[R1……Rn],请把arr调整成arr[L1,R1,L2,R2,

给定一个长度为 n 的数组,找到子集的 XOR 等于给定数字的子集数

给定一个长度为 n 的数组,找到子集的 XOR 等于给定数字的子集数

2022-01-12:给定一个正数数组arr,长度为n,下标0~n-1, arr中的0n-1位置不需要达标,它们分别是最左最右的位置, 中间位置i需要达标,达标的条件是 : arr[i-1] >

2022-05-21:给定一个数组arr,长度为n, 表示n个服务员,每个人服务一个人的时间。 给定一个正数m,表示有m个人等位。 如果你是刚来的人,请问你需要等多久? 假设:m远远大于n,比如n<=