当前位置:   article > 正文

LeetCode 2111. 使数组 K 递增的最少操作次数_使得原序列严格递增的求最小操作次数

使得原序列严格递增的求最小操作次数

一、题目

1、题目描述

  给你一个下标从 0 0 0 开始包含 n n n 个正整数的数组 arr,和一个正整数 k。如果对于每个满足 k <= i <= n-1的下标 i i i ,都有 arr[i-k] <= arr[i],那么我们称 arrk递增 的。
  每一次操作中,你可以选择一个下标 i i i 并将 arr[i]改成任意正整数。请你返回对于给定的 k,使数组变成 k递增的最少操作次数 。
  样例输入: arr = [5,4,3,2,1], k = 1
  样例输出: 4

2、基础框架

  • C语言 给出的基础框架代码如下:
int kIncreasing(int* arr, int arrSize, int k){
}
  • 1
  • 2

3、原题链接

LeetCode 2111. 使数组 K 递增的最少操作次数

二、解题报告

1、思路分析

   ( 1 ) (1) (1) 如果 k = 1 k = 1 k=1,我们是不是就是单纯的求这个数组,把它任意数改成完以后,能够得到的递增序列的最少操作数。
   ( 2 ) (2) (2) 如果 k > 1 k > 1 k>1,其实就是分别求 k k k 个序列的最少操作数。
   ( 3 ) (3) (3) 所以,只需要考虑 k = 1 k=1 k=1 的情况,对于 k > 1 k>1 k>1 的情况,其实就是调用同一个函数求解即可。
   ( 4 ) (4) (4) 对一个长度为 n n n 的序列,求出它的最长单调不降子序列,长度为 l l l,则最少操作数就是两者的差值 n − l n-l nl
   ( 5 ) (5) (5) 累加所有的差值就是最后的答案。
   ( 6 ) (6) (6) 由于数据量的限制,所以 最长单调不降子序列 只能采用 O ( n l o g n ) O(nlogn) O(nlogn) 的算法,令 g[i]表示长度为 i的最长单调不降子序列的最后一个元素的值,那么它一定是越小越好(这是一个贪心的思想)。
   ( 7 ) (7) (7) 我们来仔细思考一下这个贪心思想,对于两个长度都为 4 的 最长单调不降子序列 来说,如图所示:

  保留[0, 1, 3, 5]一定会比[1, 4, 7, 9]更优,为什么呢?比如我们来了一个 7,它能够接到 5后面,使得我的序列变长;但是,却不能接到9的后面。所以[1, 4, 7, 9]可以舍去。
   ( 8 ) (8) (8) 那么,我们真的要保留[0, 1, 3, 5]吗?答案是否定的。因为对于一个序列它一定是单调不降的,而进行状态转移的时候只需要考虑最后一个元素,所以我们只需要保存最后一个元素,也就是对于长度为 4的情况,我们只需要保留 5就可以了。这就是上文提到的 g[4]
   ( 9 ) (9) (9) 对于所有的 i i i,我们都把 g [ i ] g[i] g[i] 求出来,这样是不是就可以把最大的长度给求出来啦。
   ( 10 ) (10) (10) g [ i ] g[i] g[i] 这个数组这么定义有什么好处呢?好处就是它是一个单调不降的序列,我们可以对它进行二分查找以及插入。也就是可以通过 O(logn) 的时间复杂度快速更新 g [ i ] g[i] g[i] 的值。
   ( 11 ) (11) (11) 对于每个元素 v = a [ i ] v = a[i] v=a[i],我们需要去 g [   ] g[\ ] g[ ] 数组里面,找到一个位置 x x x,满足 v < g [ x ] v \lt g[x] v<g[x],并且 x x x 要尽量的小。由于 g [   ] g[\ ] g[ ] 是单调不降的,所以可以利用二分查找。
   ( 12 ) (12) (12) 如果对最长单调子序列还不是很明白,可以参考我的专栏:夜深人静写算法(二十)- 最长单调子序列

2、时间复杂度

   最坏时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

3、代码详解

int g[100005];   // g[i] 表示长度为 i 的最长单调不降子序列的最后一个元素的值


int maxLongestNonDescendingSeq(int *a, int size) {
    int gsize = 0;
    int i, x, v;
    int l, r, mid;
    
    for(i = 0; i < size; ++i) {
        v = a[i];
        x = -1;
        l = 0;
        r = gsize - 1;
        while(l <= r) {
            mid = (l + r) >> 1;
            if(v < g[mid]) {
                x = mid;
                r = mid - 1;
            }else {
                l = mid + 1;
            }
        }

        if(x == -1) {
            g[gsize++] = v;
        }else {
            g[x] = v;
        }
    }
    return gsize;
}

int kIncreasing(int* arr, int arrSize, int k){
    int i, j;
    int ans = 0;
    int tmp[100005], tmpSize;

    for(i = 0; i < k; ++i) {
        tmpSize = 0;
        for(j = i; j < arrSize; j += k) {
            tmp[tmpSize++] = arr[j];
        }
        ans += tmpSize - maxLongestNonDescendingSeq(tmp, tmpSize);
    }
    return ans;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

三、本题小知识

  思考:如果要求严格递增的话要怎么做呢?


四、加群须知

  相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」
  那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:

推荐阅读
相关标签