leetcode每日一题:最少翻转操作数
题目
给你一个整数 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]
相互之间独立计算。 - 将一个数组中的元素 翻转 指的是将数组中的值变成 相反顺序 。
示例 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每日一题:最少翻转操作数的更多相关文章
- 【js】Leetcode每日一题-制作m束花所需的最少天数
[js]Leetcode每日一题-制作m束花所需的最少天数 [题目描述] 给你一个整数数组 bloomDay,以及两个整数 m 和 k . 现需要制作 m 束花.制作花束时,需要使用花园中 相邻的 k ...
- 【python】Leetcode每日一题-森林中的兔子
[python]Leetcode每日一题-森林中的兔子 [题目描述] 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers ...
- [LeetCode每日一题]781. 森林中的兔子
[LeetCode每日一题]781. 森林中的兔子 问题 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers 数组里. ...
- 【JavaScript】Leetcode每日一题-在D天内送包裹的能力
[JavaScript]Leetcode每日一题-在D天内送包裹的能力 [题目描述] 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口. 传送带上的第 i 个包裹的重量为 weights[i] ...
- 【js】Leetcode每日一题-完成所有工作的最短时间
[js]Leetcode每日一题-完成所有工作的最短时间 [题目描述] 给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间. 请你将这些工作分配给 k 位工人.所有工 ...
- 【js】Leetcode每日一题-数组异或操作
[js]Leetcode每日一题-数组异或操作 [题目描述] 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == ...
- 【js】Leetcode每日一题-解码异或后数组
[js]Leetcode每日一题-解码异或后数组 [题目描述] 未知 整数数组 arr 由 n 个非负整数组成. 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encode ...
- 【JavaScript】Leetcode每日一题-青蛙过河
[JavaScript]Leetcode每日一题-青蛙过河 [题目描述] 一只青蛙想要过河. 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有). 青蛙可以跳上石子 ...
- 【JavaScript】Leetcode每日一题-平方数之和
[JavaScript]Leetcode每日一题-平方数之和 [题目描述] 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c . 示例1: 输入:c = 5 ...
- 【JavaScript】Leetcode每日一题-二叉搜索树的范围和
[JavaScript]Leetcode每日一题-二叉搜索树的范围和 [题目描述] 给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和. 示例1: 输入: ...
随机推荐
- CF div3 995 (A~G)
期末周之第三把网瘾(真是越来越放肆了...).这次赛时了一把div 3 , 又一次只做出了A~E,写完E后剩下的题没时间看了(受了些寝室噪音的干扰,最后二十分钟才出).赛后看了下F和G,感觉也是一时半 ...
- Java线程池实现原理与源码解析(jdk1.8)
为什么需要线程池?线程池能够对线程进行统一分配,调优和监控:- 降低资源消耗(线程无限制地创建,然后使用完毕后销毁)- 提高响应速度(无须创建线程)- 提高线程的可管理性 Java是如何实现和管理线程 ...
- 第三届全国高校计算机能力挑战赛-C
单项选择题 1.题 (3.0分) 以下叙述正确的是(). A.在C程序,至少要包含一个库函数 B.C程序的一行可以写多条语句 C.对一个C程序进行编译就可以生成可执行文件 D.C程序中的注释只 ...
- uni-app选中状态并改变颜色
思路 定义一个数组来记录被点击的元素 arr 数组通过indexOf来来查找 如果有,激活类就是true 没有: 激活类为false 这一步最关键的是查找的内容就是显示出来的index, 点击的时候传 ...
- 快速入门 DeepSeek-R1 大模型
国内最新的神级人工智能模型已经正式发布,没错,它就是备受瞩目的DeepSeek-R1大模型.今天,我们将对DeepSeek进行一个简单的了解,并探索如何快速使用和部署这个强大的工具.值得一提的是,De ...
- 创建json文件上传下载ftp
package org.jeecg.modules.util; import java.io.File; import java.io.FileOutputStream; import java.io ...
- Q:oracle通过正则表达式替换对应值
示例 把http://192.168.1.1:8888/a.html中的192.168.1.1:8888/替换成172.32.32.1:9999/ SELECT replace('http://192 ...
- FreeSql学习笔记——7.分组聚合
前言 分组就是将元数据通过某些条件划分为组,而聚合就是对这些组进行整合操作:在sqlserver数据库中使用的关键字group by使符合条件的集合通过某些字段分好组,再使用聚合函数(如max() ...
- 理解ID3决策树
决策树是一个树形结构,类似下面这样: 上图除了根节点外,有三个叶子节点和一个非叶子节点. 在解决分类问题的决策树中,叶子节点就表示所有的分类,比如这里的分类就有3种:无聊时阅读的邮件.需及时处理的邮件 ...
- CH340区别
CH340区别 CH340G USB转串⼝,推出时间最早,需外挂晶振,应⽤最⼴SOP16 CH340C USB转串⼝,内置晶振,引脚兼容CH340G SOP16 CH340E USB转串⼝,内置 ...