周赛传送门
排名 131,最近一段时间还挺稳定的~
这次题目还挺有意思的,尤其是最后一题,构图的方式不太容易一下想到。
2089. 找出数组排序后的目标下标
思路:遍历,计数
时间复杂度:
空间复杂度:
不难想到,排序后目标元素的下标受两个因素影响:
- 小于
target的元素的数量 - 值
target的元素的数量
显然,可通过一次遍历统计出上述两个数量,然后构造答案即可。
class Solution {
public:
vector<int> targetIndices(vector<int>& nums, int target) {
// cnt: 值 `target` 的元素的数量
// less: 小于 `target` 的元素的数量
int cnt = 0, less = 0;
for (auto num : nums) {
if (target == num) {
cnt++;
} else if (target > num) {
less++;
}
}
vector<int> res(cnt, 0);
// 构造答案
for (int i = 0 ; i < cnt; i++) {
res[i] = i+less;
}
return res;
}
};
2090. 半径为 k 的子数组平均值
思路一:前缀和
时间复杂度:
空间复杂度:
设有一维数组 pre, 存储 nums[0..i] 的累加和,这显然可以在 的时间复杂度内求出。
得到 pre 之后,以 i 为中心的子数组的平均值可表示为:
于是,不难写出如下代码啦:
class Solution {
public:
vector<int> getAverages(vector<int>& nums, int k) {
vector<int64_t> pre(nums.begin(), nums.end());
// 计算前缀和
for (int i = 1; i < pre.size(); i++) {
pre[i] += pre[i-1];
}
// res 用于存储答案
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
// 计算子数组的边界
int l = i-k, r = i+k;
if (l < 0 || r >= nums.size()) {
// 越界了,根据题意需填 -1
res.push_back(-1);
} else {
// 求解 pre[i+k] - pre[i-k-1]
// 需注意 i-k-1 为 0 的情形
int64_t sum = pre[r] - (l == 0 ? 0 : pre[l-1]);
res.push_back(sum/(k*2+1));
}
}
return res;
}
};
思路二:滑动窗口
时间复杂度:
空间复杂度:
滑动窗口的解法,省掉了用于存储前缀和的数组,仅用两个变量维护信息即可。
此解法的精髓在于,在已知以 为中心的子数组和 sum 的前提下,可通过复杂度为 的操作——sum加上 nums_{i+k} 并减去 nums{i-k-1},即可将 sum 更新为以 为中心的子数组和。
class Solution {
public:
vector<int> getAverages(vector<int>& nums, int k) {
// res 用于存储答案
vector<int> res;
// sum 表示以 i 为中心的子数组的累加和
// cnt 表示以 i 为中心的子数组的元素数量
int64_t sum = 0, cnt = min(decltype(nums.size())(k), nums.size());
// 将 sum 初始化为 nums[i,i+k-1] 的值
for (int i = 0; i < k && i < nums.size(); i++) {
sum += nums[i];
}
for (int i = 0; i < nums.size(); i++) {
// 此时 sum 为以 i-1 为中心的子数组的值,
// 尝试向 sum 添加 nums[i+k]
if (i-k-1 >= 0) {
sum -= nums[i-k-1];
cnt--;
}
// 尝试从 sum 中移除 nums[i-k-1]
if (i+k < nums.size()) {
sum += nums[i+k];
cnt++;
}
// 此时 sum 更新为以 i 中新的子数组的值
// 根据题意填充答案
if (cnt == 2*k+1) {
res.push_back(sum / cnt);
} else {
res.push_back(-1);
}
}
return res;
}
};
2091. 从数组中移除最大值和最小值
思路:贪心
时间复杂度:
空间复杂度:
假设已知两个目标元素的位置,分别记为 和 ,且满足 。下标从 0 开始。
那么有三种移除策略可选:
- 分别从两端移除——从前面移除 个元素,从后面移除 个元素。
- 均从前面移除——总共移除 个元素。
- 均从后面移除——总共移除 个元素。
这三种策略中的最优解即为答案。
class Solution {
public:
int minimumDeletions(vector<int>& nums) {
int maxNum = -1000000, maxPos = -1;
int minNum = 1000000, minPos = -1;
// 找出最小值和最大值的位置
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > maxNum) {
maxNum = nums[i];
maxPos = i;
}
if (nums[i] < minNum) {
minNum = nums[i];
minPos = i;
}
}
// 交换一下,保证 minPos < maxPos,方便后续处理
if (minPos > maxPos) {
swap(minPos, maxPos);
}
// 计算三种策略的删除次数
int s1 = minPos+1 + (nums.size() - maxPos);
int s2 = nums.size() - minPos;
int s3 = maxPos+1;
// 取其中最小值作为答案。
return min(s1, min(s2, s3));
}
};
2092. 找出知晓秘密的所有专家
思路:时间作为边权建图
时间复杂度:, 为专家人数, 为会议数,下同。
空间复杂度:
把专家视做图中的点,参加同一会议的两个专家连一条边,会议的时间作为边权。特别的,专家 和 之间连一条边,边权为 。
不难得出结论,如果专家 和 之前存在一条边权单调递增的路径,那么 必然知晓秘密。
一种可行的实现是,以 为源点,沿着边权增长的方向开始 BFS。在BFS过程中被访问的点集就是答案。
既然是BFS,那该如何定义「广度」优先呢?在最短路算法中,队列中边权累加和最小的路径会被优先处理,在此题中,不妨将路径最后一条边的权值作为整条路径的权值,即最后一条边权最小的路径被优先处理。
结合路径边权单调递增的限制,可将上述做法理解为——找出每个专家知晓秘密的最早时间点。类比最短路算法,就是找出到专家 到其他所有专家的最短路。
class Solution {
public:
vector<int> findAllPeople(int n, vector<vector<int>>& meetings, int firstPerson) {
// 构造边表
vector<vector<pair<int, int>>> edges(n);
for (const auto &meeting : meetings) {
int u = meeting[0];
int v = meeting[1];
int t = meeting[2];
edges[u].emplace_back(v, t);
edges[v].emplace_back(u, t);
}
edges[0].emplace_back(firstPerson, 0);
edges[firstPerson].emplace_back(0, 0);
// order[i] 表示 i 知晓秘密的最早时间
vector<int> order(n, -1);
// 借助优先队列,最"近"的路径总是被优先处理。
auto cmp = [](const auto &lhs, const auto &rhs) { return lhs.second > rhs.second; };
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> q(cmp);
// 将源点放入队列
q.emplace(0, 0);
// 开始BFS
while (!q.empty()) {
auto f = q.top();
q.pop();
int u = f.first;
int t = f.second;
// 已经有最优解了,跳过
if (order[u] != -1) {
continue;
}
// 记录最优解
order[u] = t;
// 寻找下一条边
for (auto &e : edges[u]) {
int np = e.first;
int nt = e.second;
if (nt < t) { continue; }
if (order[np] != -1) { continue; }
q.emplace(np, nt);
}
}
// 更新答案
vector<int> anw;
for (int i = 0; i < order.size(); i++) {
if (order[i] != -1) {
anw.emplace_back(i);
}
}
return anw;
}
};