题目

2612. 最少翻转操作数

给你一个整数 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] 之间的任意下标 ians[i] 是将 1 放到位置 i 处的 最少 翻转操作次数,如果无法放到位置 i 处,此数为 -1

  • 子数组 指的是一个数组里一段连续 非空 的元素序列。
  • 对于所有的 ians[i] 相互之间独立计算。
  • 将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序

示例 1:

输入:n = 4, p = 0, banned = [1,2], k = 4
输出:[0,-1,-1,1]
解释:k = 4,所以只有一种可行的翻转操作,就是将整个数组翻转。一开始 1 在位置 0 处,所以将它翻转到位置 0 处需要的操作数为 0 。
我们不能将 1 翻转到 banned 中的位置,所以位置 1 和 2 处的答案都是 -1 。
通过一次翻转操作,可以将 1 放到位置 3 处,所以位置 3 的答案是 1 。

示例 2:

输入:n = 5, p = 0, banned = [2,4], k = 3
输出:[0,-1,-1,-1,-1]
解释:这个例子中 1 一开始在位置 0 处,所以此下标的答案为 0 。
翻转的子数组长度为 k = 3 ,1 此时在位置 0 处,所以我们可以翻转子数组 [0, 2],但翻转后的下标 2 在 banned 中,所以不能执行此操作。
由于 1 没法离开位置 0 ,所以其他位置的答案都是 -1 。

示例 3:

输入:n = 4, p = 2, banned = [0,1,3], k = 1
输出:[-1,-1,0,-1]
解释:这个例子中,我们只能对长度为 1 的子数组执行翻转操作,所以 1 无法离开初始位置。

提示:

  • 1 <= n <= 105
  • 0 <= p <= n - 1
  • 0 <= banned.length <= n - 1
  • 0 <= banned[i] <= n - 1
  • 1 <= k <= n
  • banned[i] != p
  • banned 中的值 互不相同

思路

​ 先读懂题目,题目比较长,这里翻转的意思,就是倒序。初始情况下,数组中只有1个值是1。我们先看下下标p为1,经过1次翻转,可能到达哪些位置:

  • 如果p作为子数组的右侧端点,那么翻转后,可以达到最小值 : p - k + 1
  • 如果p作为子数组的左侧端点,那么翻转后,可以达到最大值 : p + k - 1

​ 当然,上述只是最理想的情况,由于我们子数组的长度固定为k,所以当前节点p可能无法作为子数组的左侧端点或者右侧端点,否则就会越界,对于这2种情况,我们再次分类讨论:

  • 如果 p < k - 1,此时p无法作为子数组右侧端点,可以让p移动到最左侧的子数组是[0, k-1] ,此时p移动到的位置是 k - 1 - p
  • 如果 p > n - k,此时p无法作为子数组的左侧端点,可以让p移动到最右侧的子数组是[n-k, n-1],此时p移动到的位置是 2 * n - p - 1 - k

​ 这样,我们已经获取了p经过1次翻转后,可能到达的位置范围,不过这里需要注意的是,并不是范围内每个位置都可以达到,实际上,当我们用滑动窗口滑动子数组的时候,每滑动1个位置,p的翻转位置其实移动了2。

​ 另外,我们还要去掉被ban的位置,这些位置永远不可达。

​ 那我们知道了经过1次移动,可以达到的全部位置后,我们可以使用BFS的思路,继续对上一次达到的新位置去继续遍历。注意,这里只要每次遍历新达到的位置即可,之前已经达到过的位置,既是再次达到,可以确定之前已经使用更少的翻转次数达到过。

图解

代码

优化

​ 我们在过了给出的示例提交后,发现TLE了。

​ 仔细分析一下,问题的原因是,我们每次遍历的时候,都是在[min, max]之间间隔2每次遍历全部的位置,如果k比较大,这中间位置很多,但是被ban掉的位置和之前已经达到过的位置,是不需要再次遍历的,虽然我们这里使用continue跳过了,但是还是有很大的浪费,最好可以只精确遍历未达到过的位置。

​ 由于每一次[min, max]之间达到的位置内部,奇偶性都是一致的(2次[min, max]之间奇偶性可能不同),所以,我们可以把未达到过的位置,区分奇偶性放在2个TreeSet中,然后从TreeSet中获取到[min, max]的可能值。

优化后代码

public int[] minReverseOperations(int n, int p, int[] banned, int k) {
Set<Integer> banSet = new HashSet<>();
for (int ban : banned) {
banSet.add(ban);
}
// 未决策的位置,分开奇偶,因为每次广度优先遍历,要么全是奇数、要么全是偶数
TreeSet[] oddEvenArr = new TreeSet[]{new TreeSet<Integer>(), new TreeSet<Integer>()};
for (int i = 0 ; i < n; i++) {
if (banSet.contains(i) || i == p) {
continue;
}
oddEvenArr[i&1].add(i);
}
int[] ans = new int[n];
Arrays.fill(ans, -1);
ans[p] = 0;
Queue<Integer> queue = new LinkedList<>();
queue.add(p);
while (!queue.isEmpty()) {
int cur = queue.poll();
int min, max;
if (cur < k - 1) {
min = k - 1 - cur;
} else {
min = cur - k + 1;
}
if (cur > n - k) {
max = 2 * n - k - 1 - cur;
} else {
max = cur + k - 1;
}
TreeSet<Integer> set = oddEvenArr[min&1];
Iterator<Integer> it = set.tailSet(min).iterator();
while (it.hasNext()) {
int val = it.next();
if (val > max) {
break;
}
ans[val] = ans[cur] + 1;
queue.add(val);
it.remove();
}
}
return ans;
}

耗时

leetcode每日一题:最少翻转操作数的更多相关文章

  1. 【js】Leetcode每日一题-制作m束花所需的最少天数

    [js]Leetcode每日一题-制作m束花所需的最少天数 [题目描述] 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k ...

  2. 【python】Leetcode每日一题-森林中的兔子

    [python]Leetcode每日一题-森林中的兔子 [题目描述] 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers ...

  3. [LeetCode每日一题]781. 森林中的兔子

    [LeetCode每日一题]781. 森林中的兔子 问题 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers 数组里. ...

  4. 【JavaScript】Leetcode每日一题-在D天内送包裹的能力

    [JavaScript]Leetcode每日一题-在D天内送包裹的能力 [题目描述] 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口. 传送带上的第 i 个包裹的重量为 weights[i] ...

  5. 【js】Leetcode每日一题-完成所有工作的最短时间

    [js]Leetcode每日一题-完成所有工作的最短时间 [题目描述] 给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间. 请你将这些工作分配给 k 位工人.所有工 ...

  6. 【js】Leetcode每日一题-数组异或操作

    [js]Leetcode每日一题-数组异或操作 [题目描述] 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == ...

  7. 【js】Leetcode每日一题-解码异或后数组

    [js]Leetcode每日一题-解码异或后数组 [题目描述] 未知 整数数组 arr 由 n 个非负整数组成. 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encode ...

  8. 【JavaScript】Leetcode每日一题-青蛙过河

    [JavaScript]Leetcode每日一题-青蛙过河 [题目描述] 一只青蛙想要过河. 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有). 青蛙可以跳上石子 ...

  9. 【JavaScript】Leetcode每日一题-平方数之和

    [JavaScript]Leetcode每日一题-平方数之和 [题目描述] 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c . 示例1: 输入:c = 5 ...

  10. 【JavaScript】Leetcode每日一题-二叉搜索树的范围和

    [JavaScript]Leetcode每日一题-二叉搜索树的范围和 [题目描述] 给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和. 示例1: 输入: ...

随机推荐

  1. C:.c文件和.h文件的关系

    参考:1      2 问题 在进行C语言文件移植时,遇到 "通常是每个.c文件对应一个.h文件",之前了解过.h文件是头文件,用来引用其他文件的, 但在codeblocks C语 ...

  2. 解析mysql奇葩语句

    首先看看完整的表如下图 那么看看一个比较奇葩的语句 select * from users where name = 'aa'='bb'这个语句为啥能执行成功以及为什么打印出了除了两个aa之外的所有行 ...

  3. kubernets学习笔记二

    Kubernetes部署"容器化应用" Kubernetes整体架构 何为"容器化应用"? 通俗点来说,就是你把一个程序放在docker里部署,这个docker ...

  4. Flink流处理-简单案例-01

    一.pom文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...

  5. Java 将 RTF 转换为Word、PDF、HTML、图片

    RTF文档因其跨平台兼容性而广泛使用,但有时在不同的应用场景可能需要特定的文档格式.例如,Word文档适合编辑和协作,PDF文档适合打印和分发,HTML文档适合在线展示,图片格式则适合社交媒体分享.因 ...

  6. RabbitMQ(九)——消息持久化

    RabbitMQ系列 RabbitMQ(一)--简介 RabbitMQ(二)--模式类型 RabbitMQ(三)--简单模式 RabbitMQ(四)--工作队列模式 RabbitMQ(五)--发布订阅 ...

  7. 解锁 DeepSeek 的高效秘籍:技术人员的进阶技巧

    DeepSeek 作为一款强大的代码搜索引擎,能够帮助开发者快速定位代码.解决疑难问题,甚至学习前沿技术.但想要真正释放其潜力,必须掌握以下核心技巧.本文将通过重点标注,直击高效使用 DeepSeek ...

  8. WPF 调试工具使用

    参考链接: https://supportcenter.devexpress.com/ticket/details/t720001/how-to-use-the-snoop-utility-to-in ...

  9. JDK8项目简单快速接入AI大模型(无需强制使用JDK17)

    最近AI风头越来越火,好多项目都需要接入AI接口,但Spring-AI强制绑定JDK17这些,导致以前的很多JDK8项目不太方便 找到个办法,使用AI4J来接入 1.本地部署Ollama,这个属于部署 ...

  10. const char*和char*的区别