赞
踩
给你一个下标从 0 0 0 开始包含 n n n 个正整数的数组
arr
,和一个正整数k
。如果对于每个满足k <= i <= n-1
的下标 i i i ,都有arr[i-k] <= arr[i]
,那么我们称arr
是k
递增 的。
每一次操作中,你可以选择一个下标 i i i 并将arr[i]
改成任意正整数。请你返回对于给定的k
,使数组变成k
递增的最少操作次数 。
样例输入:arr = [5,4,3,2,1], k = 1
样例输出:4
int kIncreasing(int* arr, int arrSize, int k){
}
(
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
n−l。
(
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) 如果对最长单调子序列还不是很明白,可以参考我的专栏:夜深人静写算法(二十)- 最长单调子序列。
最坏时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn) 。
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; }
思考:如果要求严格递增的话要怎么做呢?
相信看我文章的大多数都是「 大学生 」,能上大学的都是「 精英 」,那么我们自然要「 精益求精 」,如果你还是「 大一 」,那么太好了,你拥有大把时间,当然你可以选择「 刷剧 」,然而,「 学好算法 」,三年后的你自然「 不能同日而语 」。
那么这里,我整理了「 几十个基础算法 」 的分类,点击开启:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。