LeetCode周赛357
链接:https://leetcode.cn/contest/weekly-contest-357/
A题
题意:字符串当前为i即反转前面子串。
public String finalString(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == 'i') {
sb.reverse();
} else {
sb.append(s.charAt(i));
}
}
return sb.toString();
}
B题
给你一个长度为 n
的数组 nums
和一个整数 m
。请你判断能否执行一系列操作,将数组拆分成 n
个 非空 数组。
在每一步操作中,你可以选择一个 长度至少为 2 的现有数组(之前步骤的结果) 并将其拆分成 2 个子数组,而得到的 每个 子数组,至少 需要满足以下条件之一:
- 子数组的长度为 1 ,或者
- 子数组元素之和 大于或等于
m
。
如果你可以将给定数组拆分成n
个满足要求的数组,返回true
;否则,返回false
。
**注意:**子数组是数组中的一个连续非空元素序列。
思路:本质上,只要有两个连续的元素和大于等于m,则可以完成操作,所以暴力遍历一遍即可。
public boolean canSplitArray(List<Integer> nums, int m) {
if (nums.size() <= 2) {
return true;
}
for (int i = 0; i < nums.size() - 1; i++) {
if (nums.get(i) + nums.get(i + 1) >= m) {
return true;
}
}
return false;
}
C题
给你一个下标从 0 开始、大小为 n x n
的二维矩阵 grid
,其中 (r, c)
表示:
- 如果
grid[r][c] = 1
,则表示一个存在小偷的单元格 - 如果
grid[r][c] = 0
,则表示一个空单元格
你最开始位于单元格(0, 0)
。在一步移动中,你可以移动到矩阵中的任一相邻单元格,包括存在小偷的单元格。
矩阵中路径的 安全系数 定义为:从路径中任一单元格到矩阵中任一小偷所在单元格的 最小 曼哈顿距离。
返回所有通向单元格(n - 1, n - 1)
的路径中的 最大安全系数 。
单元格(r, c)
的某个 相邻 单元格,是指在矩阵中存在的(r, c + 1)
、(r, c - 1)
、(r + 1, c)
和(r - 1, c)
之一。
两个单元格(a, b)
和(x, y)
之间的 曼哈顿距离 等于| a - x | + | b - y |
,其中|val|
表示val
的绝对值。
思路:此题需要我们求满足val的最大值,可以用二分来求val。判断的时候要求得到每个点到小偷的最小曼哈顿距离大于等于val,这些点连通起来要保证起始点和终点连通,所以需要用到并查集。而求每个点到小偷的曼哈顿距离可以使用同源BFS,即遍历每个小偷每一步所覆盖的点的范围,然后更新。直至矩阵中所有的点都覆盖。
同源BFS代码:
var dis = new int[grid.size()][grid.get(0).size()];
var vis = new boolean[grid.size()][grid.get(0).size()];
var dirs = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid.get(i).size(); j++) {
if (grid.get(i).get(j) == 1) {
queue.add(new int[]{i, j});
dis[i][j] = 0;
vis[i][j] = true;
}
}
}
int cnt = 0;
while(!queue.isEmpty()) {
int size = queue.size();
cnt++;
for (int i = 0; i < size; i++) {
int[] ints = queue.poll();
for (var dir : dirs) {
int x = ints[0] + dir[0];
int y = ints[1] + dir[1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid.get(0).size() && !vis[x][y]) {
vis[x][y] = true;
dis[x][y] = cnt;
queue.add(new int[]{x, y});
}
}
}
}
离散化 + 并查集+二分代码:
private int[] fa;
private int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
private void merge(int x, int y) {
if (find(x) != find(y)) {
fa[find(x)] = find(y);
}
}
int n = grid.get(0).size();
fa = new int[n * n];
int l = 1, r = 400;
int len = 0;
while(l <= r) {
for (int i = 0; i < n * n; i++) {
fa[i] = i;
}
vis = new boolean[grid.size()][grid.get(0).size()];
int mid = (l + r) >> 1;
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid.get(i).size(); j++) {
if (dis[i][j] < mid) {
continue;
}
if (j != 0 && vis[i][j - 1]) {
merge(i * n + j, i * n + j - 1);
}
if (i != 0 && vis[i - 1][j]) {
merge(i * n + j, (i - 1) * n + j);
}
vis[i][j] = true;
}
}
if (vis[grid.size() - 1][grid.get(0).size() - 1] && find(0) == find((grid.size() - 1) * n + grid.get(0).size() - 1)) {
l = mid + 1;
len = mid;
} else {
r = mid - 1;
}
}
return len;
完整代码如下:
private int[] fa;
private int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
private void merge(int x, int y) {
if (find(x) != find(y)) {
fa[find(x)] = find(y);
}
}
public int maximumSafenessFactor(List<List<Integer>> grid) {
Deque<int[]> queue = new LinkedList<>();
var dis = new int[grid.size()][grid.get(0).size()];
var vis = new boolean[grid.size()][grid.get(0).size()];
var dirs = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid.get(i).size(); j++) {
if (grid.get(i).get(j) == 1) {
queue.add(new int[]{i, j});
dis[i][j] = 0;
vis[i][j] = true;
}
}
}
int cnt = 0;
while(!queue.isEmpty()) {
int size = queue.size();
cnt++;
for (int i = 0; i < size; i++) {
int[] ints = queue.poll();
for (var dir : dirs) {
int x = ints[0] + dir[0];
int y = ints[1] + dir[1];
if (x >= 0 && x < grid.size() && y >= 0 && y < grid.get(0).size() && !vis[x][y]) {
vis[x][y] = true;
dis[x][y] = cnt;
queue.add(new int[]{x, y});
}
}
}
}
int n = grid.get(0).size();
fa = new int[n * n];
int l = 1, r = 400;
int len = 0;
while(l <= r) {
for (int i = 0; i < n * n; i++) {
fa[i] = i;
}
vis = new boolean[grid.size()][grid.get(0).size()];
int mid = (l + r) >> 1;
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid.get(i).size(); j++) {
if (dis[i][j] < mid) {
continue;
}
if (j != 0 && vis[i][j - 1]) {
merge(i * n + j, i * n + j - 1);
}
if (i != 0 && vis[i - 1][j]) {
merge(i * n + j, (i - 1) * n + j);
}
vis[i][j] = true;
}
}
if (vis[grid.size() - 1][grid.get(0).size() - 1] && find(0) == find((grid.size() - 1) * n + grid.get(0).size() - 1)) {
l = mid + 1;
len = mid;
} else {
r = mid - 1;
}
}
return len;
}
此题也可以通过遍历哈曼顿距离所对应的点来求值,当前曼哈顿距离下,对应的点做一遍并查集,然后判断是否满足起始点和重点在同一个集合内,如果满足直接输出当前值即可。
D题
题意:给你一个长度为 n
的二维整数数组 items
和一个整数 k
。
items[i] = [profiti, categoryi]
,其中 profiti
和 categoryi
分别表示第 i
个项目的利润和类别。
现定义 items
的 子序列 的 优雅度 可以用 total_profit + distinct_categories2
计算,其中 total_profit
是子序列中所有项目的利润总和,distinct_categories
是所选子序列所含的所有类别中不同类别的数量。
你的任务是从 items
所有长度为 k
的子序列中,找出 最大优雅度 。
用整数形式表示并返回 items
中所有长度恰好为 k
的子序列的最大优雅度。
注意: 数组的子序列是经由原数组删除一些元素(可能不删除)而产生的新数组,且删除不改变其余元素相对顺序。
思路:也就是数组中任选k个元素,满足元素的val + 元素种类的平方和最大。
对元素的val从大到小排序,并选前k个。在保证元素的val和为最大的情况下,然后进行反悔贪心操作。对于下一个可取的元素而言,若它是一个新的种类,并且之前所取的元素中,有种类重复的元素,那么我们添加它,并且删除之前种类重复元素中,val最小的一个。重复操作,选择所有结果的最大值即是最优解。
代码如下:
public long findMaximumElegance(int[][] items, int k) {
Arrays.sort(items, (a, b) -> b[0] - a[0]);
long mx = 0;
long preSum = 0;
Set<Integer> set = new HashSet<>();
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < items.length; i++) {
if (i < k) {
preSum += items[i][0];
if (!set.add(items[i][1])) {
stack.push(items[i][0]);
}
} else if (!stack.isEmpty() && set.add(items[i][1])) {
preSum += items[i][0] - stack.pop();
}
mx = Math.max(mx, preSum + (long) set.size() * set.size());
}
return mx;
}