package com.algorithm.level2.dp.lis;
// 使数组K递增的最少操作次数
// 给你一个下标从0开始包含n个正整数的数组arr,和一个正整数k
// 如果对于每个满足 k <= i <= n-1 的下标 i
// 都有 arr[i-k] <= arr[i] ,那么称 arr 是K递增的
// 每一次操作中,你可以选择一个下标i并将arr[i]改成任意正整数
// 请你返回对于给定的 k ,使数组变成K递增的最少操作次数
// 测试链接 : https://leetcode.cn/problems/minimum-operations-to-make-the-array-k-increasing/
public class Level2_DP_LIS_003_MinimumOperationsToMakeArraykIncreasing {
public static int MAXN = 100001;
public static int[] nums = new int[MAXN];
public static int[] ends = new int[MAXN];
public static int kIncreasing(int[] arr, int k) {
int n = arr.length;
int ans = 0;
// 如果对于每个满足 k <= i <= n-1 的下标 i ,都有 arr[i-k] <= arr[i] ,那么我们称 arr 是 K 递增 的。
// 这里是对每个满足的i,i是变量,i不是从0开始的,注意
for (int i = 0, size; i < k; i++) {
size = 0;
// 把每一组的数字放入容器
// 将数组按索引模 k 分成 k 组,每组包含从索引 i, i+k, i+2k, ... 的元素。
// 按索引模 k 分组的原因是题目要求每组内的元素形成一个不下降子序列
// 分组后,每组的元素在原数组中是间隔开的(每隔 k 个元素),因此它们之间没有顺序约束。
for (int j = i; j < n; j += k) {
nums[size++] = arr[j];
}
// 当前组长度 - 当前组最长不下降子序列长度 = 当前组至少需要修改的数字个数
// 对于每组,调用 lengthOfNoDecreasing 函数计算该组的 LIS 长度。
// 统计需要修改的元素个数 :
// 每组中需要修改的元素个数 = 组的总长度 - LIS 长度。
// 累加所有组的结果。
ans += size - lengthOfNoDecreasing(size);
}
return ans;
}
// nums[0...size-1]中的最长不下降子序列长度,计算最长不下降子序列(LIS)
public static int lengthOfNoDecreasing(int size) {
int len = 0;
for (int i = 0, find; i < size; i++) {
// 二分查找
find = bs(len, nums[i]);
if (find == -1) {
ends[len++] = nums[i];
} else {
ends[find] = nums[i];
}
}
return len;
}
public static int bs(int len, int num) {
int l = 0, r = len - 1, m, ans = -1;
while (l <= r) {
m = (l + r) / 2;
if (num < ends[m]) {
ans = m;
r = m - 1;
} else {
l = m + 1;
}
}
return ans;
}
}