我是靠谱客的博主 虚心冬日,最近开发中收集的这篇文章主要介绍头歌 2022 春第一期 Greeker‘s party 13 周,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

头歌 2022 春第一期 Greeker's party 13 周

    • 第 1 关:疫情拐点 I
    • 第 2 关:疫情拐点 II
    • 第 3 关:疫情拐点 III

第 1 关:疫情拐点 I

疫情已经持续数年。最近,A 国再次出现新一轮疫情高峰,B 团队正在尝试寻找拐点。该团队对拐点有如下定义:对于连续的三天,即第 j 天,j + 1 天,j + 2 天,报告的感染者数字 p i p_i pi 满足 p j + 1 > p j p_{j + 1} gt p_j pj+1>pj p j + 1 > p j + 2 p_{j + 1} gt p_{j + 2} pj+1>pj+2。如果存在拐点,则输出 "True",否则输出 "False"

直来直去,判断每一个 可能 是拐点的点(即下标范围在 1 ~ (m - 1))是不是真正的拐点。核心代码如下:

bool flag = false;
for (int i = 1; i < m - 1; i++)
if (nums[i] > nums[i - 1] && nums[i] > nums[i + 1]) {
flag = true;
break;
};
cout << (flag ? "True" : "False") << ' ';

时间复杂度是 O ( m ) O(m) O(m),空间复杂度是 O ( m ) O(m) O(m),完整代码见 GitHub

第 2 关:疫情拐点 II

该团队对拐点的定义方法进行了修改,现在调整为,存在三天(不需要连续)满足中间一天报告的感染者数字最大,前面的一天报告的感染者数字居中,最后一天报告的感染者数字最少,即存在 i < j < k i lt j lt k i<j<k 满足 p j > p i > p k p_j gt p_i gt p_k pj>pi>pk i , j , k i, j, k i,j,k 不需要连续。

思考再三,我们可以遍历数组,过程中,保存当前选取的最优的 p i p_i pi p j p_j pj,最优的意思是 p j p_j pj p i p_i pi 尽可能大,这样才更有利于找到一个小的 p k p_k pk

具体地,我们以 nums[0] p i p_i pi 记为 numi,然后以从下标为 1 开始向后遍历的首个 大于 nums[0] 的数为 p j p_j pj 记为 numj,这样就完成了 初始化

int numi = nums[0], numj, idx = 1;
while (idx < m && nums[idx] <= nums[idx - 1]) idx++;
if (idx < m) numj = nums[idx];

于是我们继续遍历剩下的数组,当遍历到 nums[idx] 时,如果 nums[idx] > numj 的话,则我们遵循之前的原则尽可能让 numinumj 大,所以同时更新 numi -> numjnumj -> nums[idx];如果 nums[idx] < numi 的话,则我们神奇地发现已经找到了这样的三元组。

bool flag = false;
while (++idx < m) {
if (nums[idx] > numj) {
numi = numj;
numj = nums[idx];
} else if (nums[idx] < numi) {
flag = true;
break;
} else continue;
};
cout << (flag ? "True" : "False") << ' ';

时间复杂度是 O ( m ) O(m) O(m),空间复杂度是 O ( m ) O(m) O(m),完整代码见 GitHub

第 3 关:疫情拐点 III

该团队对拐点的定义方法进行了修改,现在调整为,存在三天(不需要连续)满足中间一天报告的感染者数字最大,前面的一天报告的感染者数字居中,最后一天报告的感染者数字最少,即存在 i < j < k i lt j lt k i<j<k 满足 p j > p i > p k p_j gt p_i gt p_k pj>pi>pk i , j , k i, j, k i,j,k 不需要连续。

看上去很难的样子,我也只能想到相对来说较为复杂的做法,仅供参考吧。而且我实在是太懒了,所以我就用了 <vector><sort> 这两个 STL 库,各位小伙伴如果有好的解法欢迎与我交流鸭。

假设我们需要判断第 i 天是不是拐点,那么就要在第 0 ~ (i - 1) 天找到小于 nums[i] 的最大值记为 left,然后在第 (i + 1) ~ (m - 1) 天试图找到小于 left 的值记为 right,如果 right 存在就说明第 i 天是拐点。

一看数据范围 10^5 情况不妙,双循环 O ( m 2 ) O(m^2) O(m2) 复杂度肯定超时,所以考虑用数组来维护,即对于第 i 天我们将维护一个左数组 pre 包含 0 ~ (i - 1) 的所有值,一个右数组 post 包含 (i + 1) ~ (m - 1) 的所有值,注意 prepost 数组都是单调递增的,因此我们可以二分查找,在 O ( l o g ( m ) ) O(log(m)) O(log(m)) 复杂度找到 leftright

在遍历第 i 天前,我们需要将 nums[i]post 数组中删除,在遍历第 i 天后,我们需要将 nums[i] 插入 pre 数组中,注意,删除和插入操作都需要保证维护 prepost 的有序性,因此需要先二分查找到删除和插入的位置再进行对应操作。

STL 的加持下,我们只需要用到 lower_bound 函数便能快速查找到大于等于某个值的第一个元素的位置。而 sort 能对整个数组进行排序,insert 则可以在数组某个位置插入一个新值。

int count = 0;
for (int i = 0; i < m; i++) {
post.erase(lower_bound(post.begin(), post.end(), nums[i])); // 从 post 数组删去 nums[i]
auto pre_iter = lower_bound(pre.begin(), pre.end(), nums[i]); // 找到第一个大于等于 nums[i] 的数,返回其迭代器
if (pre_iter != pre.begin()) {
--pre_iter; // 找到小于 nums[i] 的值最大的数
int left = *pre_iter;
auto post_iter = lower_bound(post.begin(), post.end(), left); // 在 post 数组尝试找到比 left 小的数 right
if (post_iter != post.end()) // 如果找到了这个数就代表这一天是拐点
++count;
};
pre.insert(lower_bound(pre.begin(), pre.end(), nums[i]), nums[i]);
};

时间复杂度是 O ( m l o g ( m ) ) O(mlog(m)) O(mlog(m)),空间复杂度是 O ( m ) O(m) O(m),完整代码见 GitHub

我觉得本次周赛的题目出的很不错,三道题的连贯性和区分度都是一流的,但不得不吐槽一下,禁用 STL 库是不合理的。俺知道此次比赛面向的同学主要是大一的,直接使用 STL 库可能会降低小朋友们对数据结构与算法的底层原理的掌握程度,但是在未来的学习与工作中,通常情况下,使用现成的轮子是比较好的做法。

我能理解禁用 STL 库的初衷,但我不赞成禁用 STL 库的做法,由于我刷题是为了保持写代码的手感,所以必要的时候还是会适当运用一些 STL 库里的东西。当然,我还是建议还在学《数据结构》课程的小朋友们,有空有兴趣的话,跟着课程进度自己实现一些基础的数据结构的模板吧,比如 vectorstackbinaryTree 这种,毕竟 20 年疫情在家上网课的时候,我也都写过一遍呢。

最近在看《STL 源码分析》,坚持就是胜利!

好啦,以上就是第十二周周赛的全部内容,欢迎关注我的 GitHub 账号。

最后

以上就是虚心冬日为你收集整理的头歌 2022 春第一期 Greeker‘s party 13 周的全部内容,希望文章能够帮你解决头歌 2022 春第一期 Greeker‘s party 13 周所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部