赛题传送门
赛题
给你一个二维整数数组
nums
,其中nums[i]
是由 不同 正整数组成的一个非空数组,按 升序排列 返回一个数组,数组中的每个元素在nums
所有数组 中都出现过。
提示:
1 <= nums.length <= 1000
1 <= sum(nums[i].length) <= 1000
1 <= nums[i][j] <= 1000
nums[i]
中的所有值 互不相同
作为力扣竞赛的签到题,那必然是可以用 暴力 的方法写出来的。对于每个出现在任意 nums[i]
中的数 num
,我们去判断 num
是否出现在所有数组中,如果出现则将其压入结果数组 answer
,否则舍弃即可。最后使用 sort
对结果数组进行排序即可。这样的时间复杂度高达 O ( N 2 ) O(N^2) O(N2),虽然能过测试,但很憋屈。
于是考虑优化,看到提示中给出 nums[i]
中的所有值 互不相同,所以一个元素在所有数组中都出现过就等价于这个元素的出现总次数大于等于数组的个数。所有,我们用 unordered_map
统计元素在所有数组中的出现次数即可。
unordered_map counts;
for (const vector& num : nums)
for (const int& nn : num)
counts[nn]++;
然后将出现次数大于等于数组个数的元素放入结果数组 answer
并用 sort
排序。
vector answer;
for (const auto& iter : counts)
if (iter.second >= n)
answer.push_back(iter.first);
sort(answer.begin(), answer.end());
完整代码见 GitHub。
赛题
给你一个二维整数数组
circles
,其中circles[i] = [xi, yi, ri]
表示网格上圆心为(xi, yi)
且半径为ri
的第i
个圆,返回出现在 至少一个 圆内的 格点数目。格点 是指整数坐标对应的点,圆周上的点 也被视为出现在圆内的点。
提示:
1 <= circles.length <= 200
circles[i].length == 3
1 <= xi, yi <= 100
1 <= ri <= min(xi, yi)
一看到这种覆盖问题就头疼,但一看数据范围,瞬间乐开了花。因为半径和圆心位置的横纵坐标都小于 100 100 100,所以总共最多也就 200 * 200
个格点可能出现在圆内。可以看到给出的圆的个数也少于 200 200 200,那么就可以对于每个格点依次判断是否出现在各个圆内,这样时间复杂度是 O ( N 3 ) , N ≤ 200 O(N^3), N \leq 200 O(N3),N≤200。
int ans = 0;
for (int i = 0; i < 200; i++)
for (int j = 0; j < 200; j++)
for (const vector& cir : circles) {
int x = cir[0], y = cir[1], r = cir[2];
if ((x - i) * (x - i) + (y - j) * (y - j) <= r * r) {
ans++;
break;
};
};
return ans;
完整代码见 GitHub。
赛题
题目简单概括如下,给你多个左下角在 (0, 0)
右上角在 (li, hi)
的矩形,再给你一组坐标为 (xi, yi)
的点,计算每个点出现在多少个矩形中。
好吧,又是覆盖问题,一看赞助商是华为,这竞赛属实是力扣专题训练了。最简单的想法就是 暴力遍历,当然时间复杂度高达 O ( N 2 ) O(N^2) O(N2) 会超时。
感觉很难,想了好一会,思路都在往固定长度或者宽度然后同时遍历 rectangles
和 points
来优化时间复杂度。最终目光锁定在这个提示上:1 <= hi, yj <= 100
。高度范围很小意味着时间复杂度里可能有他,也就是说,可能会遍历每个高度。
所以,我们可以先对 rectangles
和 points
按照长度从小到大进行排序,注意记录 points
对应元素的下标,我是直接先将下标 push
到 points
数组元素中去的。
sort(rectangles.begin(), rectangles.end(), [](const vector& a, const vector& b) {
return a[0] < b[0];
});
for (int i = 0; i < points.size(); ++i)
points[i].push_back(i);
sort(points.begin(), points.end(), [](const vector& a, const vector& b) {
return a[0] < b[0];
});
然后,我们可以将 points
中元素按照高度分门别类存好,方便后面直接取用。
vector>> pp(100 + 1);
for (const vector& point : points)
pp[point[1]].push_back(point);
然后,对于每个高度 hi
,我们先将 rectangles
中元素大于等于 hi
的取出。并用 双指针 同时遍历部分 rectangles
数组和部分 points
数组。注意,当某个高度 hi
对应的 points
为空时,可以直接跳过,这一步优化能让运行时间下降 3 ∼ 4 3 \sim 4 3∼4 倍,才能顺利通过测试。
for (int h = 1; h <= 100; h++) {
if (pp[h].empty()) continue;
vector> new_rec;
for (const vector& rec : rectangles)
if (rec[1] >= h)
new_rec.push_back(rec);
rectangles = new_rec;
int i = 0;
for (const vector& v : pp[h]) {
int l = v[0], h = v[1], idx = v[2], n = rectangles.size();
while (i < n && rectangles[i][0] < l) i++;
answer[idx] = n - i;
};
};
完整代码见 GitHub。
赛题
给你一个下标从 0 开始的二维整数数组
flowers
,其中flowers[i] = [start_i, end_i]
表示第i
朵花的 花期 从start_i
到end_i
(都包含)。同时给你一个下标从 0 开始大小为n
的整数数组persons
,persons[i]
是第i
个人来看花的时间。
请你返回一个大小为n
的整数数组answer
,其中answer[i]
是第i
个人到达时在花期内花的数目。
这种区间覆盖并求单点累加值的问题太过于常规,主流做法往往是使用高中竞赛中的 线段树,而这道题就是最基础的线段树板子就可以解决的问题。但是,我没打过高中的 OI 竞赛,对于线段树也仅仅停留在了解功能加会用的阶段,却并不会写。而且,我自认为在未来的工作学习中,线段树的思想可以借鉴,但不必强求自己熟练地掌握写法。因此,我选择换一种思路来解决这道题。
由于每个花期相当于是给闭区间 [start_i, end_i]
中所有元素的值加一,那么我们可以用一个数组 up_down
去表示所有的 起伏点,具体地
for (const vector& fl : flowers) {
int start = fl[0], end = fl[1];
up_down[start]++;
up_down[end + 1]--;
};
之后我们从头去累加这个数组,就可以模拟出相当于在 [start_i, end_i]
中元素均加一的效果。
但是,盲生你发现了华点,那就是 start_i
和 end_i
的范围高达 1 0 9 10^9 109,这个 up_down
的数组开不了这么大。因此,考虑使用哈希表替换数组来记录起伏点,并且不再逐个遍历每个值,而是挑 persons[i]
的位置进行遍历。
vector pos;
for (const auto iter : up_down)
pos.push_back(iter.first);
sort(pos.begin(), pos.end());
sort(persons.begin(), persons.end());
int idx = 0, cnt = 0, n = pos.size();
for (const int& person : persons) {
while (idx < n && pos[idx] <= person) {
cnt += up_down[pos[idx]];
idx++;
};
tmp_answer[person] = cnt;
};
完整代码见 GitHub。
这场周赛是我在 2022-4-24
上午准时参加的力扣竞赛,在第三题优化常数时间复杂度上多花了些时间,其他几道题都没给我造成什么很大的困难。我昨天还参加了力扣的 2022 春季赛团队赛,带着俺的女朋友打的,她也做出来了 1 道题,真棒!我解决了 3 道题,中间插播了一段大创答辩耽误了三四十分钟吧,导致凑正方体那道题没有 debug
完,遗憾败北,不过还是取得了一个不错的成绩。这两天在做 Chcore 的 lab4,调试多线程快要疯掉了,等过两天有空了我再来把春季赛的题解写一写,分享一下经验。
好滴,以上就是力扣第 220 场周赛的全部思路啦。最后,欢迎关注我的 GitHub 账号。