package class_2023_05_2_week; import java.util.Arrays; import java.util.TreeSet; // 给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p , // 它们表示一个长度为 n 且下标从 0 开始的数组 arr , // 数组中除了下标为 p 处是 1 以外,其他所有数都是 0 。 // 同时给你一个整数数组 banned ,它包含数组中的一些位置。 // banned 中第 i 个位置表示 arr[banned[i]] = 0 ,题目保证 banned[i] != p 。 // 你可以对 arr 进行 若干次 操作。一次操作中,你选择大小为 k 的一个 子数组 // 并将它 翻转 。在任何一次翻转操作后, // 你都需要确保 arr 中唯一的 1 不会到达任何 banned 中的位置。 // 换句话说,arr[banned[i]] 始终 保持 0 。 // 请你返回一个数组 ans ,对于 [0, n - 1] 之间的任意下标 i , // ans[i] 是将 1 放到位置 i 处的 最少 翻转操作次数, // 如果无法放到位置 i 处,此数为 -1 。 // 子数组 指的是一个数组里一段连续 非空 的元素序列。 // 对于所有的 i ,ans[i] 相互之间独立计算。 // 将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序 。 // 测试链接 : https://leetcode.cn/problems/minimum-reverse-operations/ public class Code05_MinReverseOperations { public static int[] minReverseOperations(int n, int p, int[] banned, int k) { TreeSet oddSet = new TreeSet<>(); TreeSet evenSet = new TreeSet<>(); for (int i = 1; i < n; i += 2) { oddSet.add(i); } for (int i = 0; i < n; i += 2) { evenSet.add(i); } for (int ban : banned) { oddSet.remove(ban); evenSet.remove(ban); } oddSet.remove(p); evenSet.remove(p); int[] ans = new int[n]; Arrays.fill(ans, -1); int[] queue = new int[n]; int l = 0; int r = 0; queue[r++] = p; int level = 0; while (l < r) { int end = r; for (; l < end; l++) { int cur = queue[l]; ans[cur] = level; // 核心的两句,举例子说明 // 纯下标变换,没什么算法 int left = Math.max(cur - k + 1, k - cur - 1); int right = Math.min(cur + k - 1, n * 2 - k - cur - 1); TreeSet curSet = (left & 1) == 1 ? oddSet : evenSet; Integer ceilling = curSet.ceiling(left); while (ceilling != null && ceilling <= right) { queue[r++] = ceilling; curSet.remove(ceilling); ceilling = curSet.ceiling(left); } } level++; } return ans; } }