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;}}