leetcode contest 323
周赛的题目又是大放水了,感觉双周赛的题目比较难,周赛的题目放水的概率非常高。
6257. 删除每行中的最大值
给你一个 m x n
大小的矩阵 grid
,由若干正整数组成。
执行下述操作,直到 grid
变为空矩阵:
- 从每一行删除值最大的元素。如果存在多个这样的值,删除其中任何一个。
- 将删除元素中的最大值与答案相加。
注意 每执行一次操作,矩阵中列的数据就会减 1 。
返回执行上述操作后的答案。
示例 1:
输入:grid = [[1,2,4],[3,3,1]] |
示例 2:
输入:grid = [[10]] |
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
1 <= grid[i][j] <= 100
地址
https://leetcode.cn/contest/weekly-contest-323/problems/delete-greatest-value-in-each-row/
题意
直接遍历
思路
- 将矩阵的每一行按照从小到大排序,每次我们取出每一列的最大值即可。
- 复杂度分析:
- 时间复杂度:$O(mn\log n)$。
- 空间复杂度:$O(\log)$。
代码
class Solution { |
6258. 数组中最长的方波
给你一个整数数组 nums
。如果 nums
的子序列满足下述条件,则认为该子序列是一个 方波 :
- 子序列的长度至少为
2
,并且 - 将子序列从小到大排序 之后 ,除第一个元素外,每个元素都是前一个元素的 平方 。
返回 nums
中 最长方波 的长度,如果不存在 方波 则返回 -1
。
子序列 也是一个数组,可以由另一个数组删除一些或不删除元素且不改变剩余元素的顺序得到。
示例 1 :
输入:nums = [4,3,6,16,8,2] |
示例 2 :
输入:nums = [2,3,5,6,7] |
提示:
2 <= nums.length <= 105
2 <= nums[i] <= 105
地址
https://leetcode.cn/contest/weekly-contest-323/problems/longest-square-streak-in-an-array/
题意
动态规划
思路
- 由于题目中方波数组的顺序必须满足从小到大排列,我们设 $dp[x]$ 表示以 $x$ 为结尾的方波数组的最长长度,则 $dp[x] = dp[\sqrt{x}] + 1$。我们找到最长的方波数组长度即可。
- 复杂度分析:
- 时间复杂度:$O(n \log n)$,其中 $n$ 为数组的元素个数。
- 空间复杂度:$O(\log n)$,其中 $n$ 为数组的元素的个数。
代码
class Solution { |
6259. 设计内存分配器
给你一个整数 n
,表示下标从 0 开始的内存数组的大小。所有内存单元开始都是空闲的。
请你设计一个具备以下功能的内存分配器:
- 分配 一块大小为
size
的连续空闲内存单元并赋 idmID
。 - 释放 给定 id
mID
对应的所有内存单元。
注意:
- 多个块可以被分配到同一个
mID
。 - 你必须释放
mID
对应的所有内存单元,即便这些内存单元被分配在不同的块中。
实现 Allocator
类:
Allocator(int n)
使用一个大小为n
的内存数组初始化Allocator
对象。int allocate(int size, int mID)
找出大小为size
个连续空闲内存单元且位于 最左侧 的块,分配并赋 idmID
。返回块的第一个下标。如果不存在这样的块,返回-1
。int free(int mID)
释放 idmID
对应的所有内存单元。返回释放的内存单元数目。
示例:
输入 |
提示:
1 <= n, size, mID <= 1000
- 最多调用
allocate
和free
方法1000
次
地址
https://leetcode.cn/contest/weekly-contest-323/problems/design-memory-allocator/
题意
直接模拟
思路
- 由于给定的数据量太小,直接暴力模拟即可,没有什么太大的难度,这个题目出的比较失败,至少应该将数量级到达 $10^4$ 以上。
- 复杂度分析
- 时间复杂度:时间复杂度为 $O(nm)$,$n$ 表示给定的数组的长度, $m$ 表示调用的函数的次数。
- 空间复杂度:空间复杂度为 $O(n)$,$n$ 表示给定的数组的长度。
代码
class Allocator { |
6260. 矩阵查询可获得的最大分数
给你一个大小为 m x n
的整数矩阵 grid
和一个大小为 k
的数组 queries
。
找出一个大小为 k
的数组 answer
,且满足对于每个整数 queres[i]
,你从矩阵 左上角 单元格开始,重复以下过程:
- 如果
queries[i]
严格 大于你当前所处位置单元格,如果该单元格是第一次访问,则获得 1 分,并且你可以移动到所有4
个方向(上、下、左、右)上任一 相邻 单元格。 - 否则,你不能获得任何分,并且结束这一过程。
在过程结束后,answer[i]
是你可以获得的最大分数。注意,对于每个查询,你可以访问同一个单元格 多次 。
返回结果数组 answer
。
示例 1:
输入:grid = [[1,2,3],[2,5,7],[3,5,1]], queries = [5,6,2] |
示例 2:
输入:grid = [[5,2,1],[1,1,2]], queries = [3] |
提示:
m == grid.length
n == grid[i].length
2 <= m, n <= 1000
4 <= m * n <= 105
k == queries.length
1 <= k <= 104
1 <= grid[i][j], queries[i] <= 106
地址
https://leetcode.cn/contest/weekly-contest-323/problems/maximum-number-of-points-from-grid-queries/
题意
离线查询 + 优先队列
思路
- 题目出的不是很好,感觉太过于模板的题目,即且翻译的有问题,简单的来说即给定的 $x$,从 $(0,0)$ 出发可以遍历的所有小于 $x$ 的元素的数目。
- 本题的解法有多种,首先我们只需将所有的查询从小到大进行排序,从而我们可以将矩阵从小到大进行遍历。
- 优先队列: 我们从 $(0,0)$ 压入到优先队列,每次查询 $x$ 时,我们将队列中所有严格小于 $x$ 的元素 $(x,y)$ 弹出并计数,并同时将 $(x,y)$ 四个方向上相邻的元素压入到队列中,这样保证队列中待访问且所有符合要求的元素全部从队列中弹出。我们计数所有当前已弹出的元素为 $tot$,满足小于 $x$ 的元素的个数即为 $tot$。
- 并查集: 将矩阵所有的元素按照从小到大进行排序,每次查询 $x$ 时将所有小于 $x$ 的矩阵中的元素与其相邻的边进行聚合,最后我们查询 $(0,0)$ 所在的连通单元的单元的数目即可。
- 复杂度分析:
- 时间复杂度:$O(q \log q + q \times (\log mn))$,其中 $q$ 表示查询的次数,$m,n$ 表示矩阵的行与列。
- 空间复杂度:$O(mn)$,其中 $m,n$ 表示矩阵的行与列。
代码
- 优先队列
class Solution {
public:
vector<int> maxPoints(vector<vector<int>>& grid, vector<int>& queries) {
int m = grid.size();
int n = grid[0].size();
int k = queries.size();
vector<int> res(k);
vector<pair<int,int>> arr;
for (int i = 0; i < k; i++) {
arr.emplace_back(queries[i], i);
}
sort(arr.begin(), arr.end());
vector<vector<bool>> visit(m, vector<bool>(n, false));
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, greater<tuple<int,int,int>>> pq;
pq.emplace(grid[0][0], 0, 0);
int tot = 0;
visit[0][0] = true;
int dir[4][2] = {{0, -1}, {0, 1}, {1, 0}, {-1, 0}};
for (int i = 0; i < k; i++) {
while(!pq.empty() && get<0>(pq.top()) < arr[i].first) {
auto [val, x, y] = pq.top();
pq.pop();
tot++;
for (int j = 0; j < 4; j++) {
int nx = x + dir[j][0];
int ny = y + dir[j][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n) {
if (visit[nx][ny]) continue;
pq.emplace(grid[nx][ny], nx, ny);
visit[nx][ny] = true;
}
}
}
res[arr[i].second] = tot;
}
return res;
}
}; - 并查集欢迎关注和打赏,感谢支持!
class Solution {
const int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public:
vector<int> maxPoints(vector<vector<int>> &grid, vector<int> &queries) {
int m = grid.size(), n = grid[0].size(), mn = m * n;
// 并查集模板
int fa[mn], size[mn];
iota(fa, fa + mn, 0);
fill(size, size + mn, 1);
function<int(int)> find = [&](int x) -> int { return fa[x] == x ? x : fa[x] = find(fa[x]); };
auto merge = [&](int from, int to) {
from = find(from);
to = find(to);
if (from != to) {
fa[from] = to;
size[to] += size[from];
}
};
// 矩阵元素从小到大排序,方便离线
array<int, 3> a[mn];
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
a[i * n + j] = {grid[i][j], i, j};
sort(a, a + mn);
// 查询的下标按照查询值从小到大排序,方便离线
int k = queries.size(), id[k];
iota(id, id + k, 0);
sort(id, id + k, [&](int i, int j) { return queries[i] < queries[j]; });
vector<int> ans(k);
int j = 0;
for (int i : id) {
int q = queries[i];
for (; j < mn && a[j][0] < q; ++j) {
int x = a[j][1], y = a[j][2];
// 枚举周围四个格子,值小于 q 才可以合并
for (auto &d : dirs) {
int x2 = x + d[0], y2 = y + d[1];
if (0 <= x2 && x2 < m && 0 <= y2 && y2 < n && grid[x2][y2] < q)
merge(x * n + y, x2 * n + y2); // 把坐标压缩成一维的编号
}
}
if (grid[0][0] < q)
ans[i] = size[find(0)]; // 左上角的连通块的大小
}
return ans;
}
}; - 关注我的博客: http://whistle-wind.com/
- 关注我的知乎:https://www.zhihu.com/people/da-hua-niu
- 关注我的微信公众号: 公务程序猿
扫描二维码,分享此文章