[CP] 刷题小记

简单记录一下刷题过程中学到的 trick 以及容易犯错的地方。

只是写给自己看的,有些地方因为没有附带题目背景或条件,看起来会很突兀。

提取英文字母的编号

char c = 'a';
int id = c & 31;

不区分大小写。a / A 返回 1,z / Z 返回 26,以此类推。

双指针分段 + 哨兵

以前写分段都要特别处理末尾一段的情况,非常麻烦。今天突然意识到,往末尾加一个哨兵就能简化这一流程。

寻找最小值和次小值

if a[x] < minVal:
subMinVal = minVal
minVal = a[x]
else:
subMinVal = min(subMinVal, a[x])

当找到新的最小值时,旧的最小值会成为次小值——这一步总是忘记。

需要注意的是,subMinVal = minVal 仅适用于一般的情况,如当前有多组数据,需要找到每一组数据的最小值以及最小(全局)的次小值(见 3143. Maximum Points Inside the Square),需要改成 subMinVal = min(subMinVal, a[x]),因为全局次小值有可能比当前数据对应的最小值还要小。

子数组的数量

假设数组长度为 n,那么其子数组数量为:

n(n+1)2=Cn2+n

总是写成 Cn2……

滑动窗口统计子数组数量

假设滑动窗口左右指针为 L、R,目标子数组满足的条件为 PP 的真值随着子数组的长度呈现出单调性且只有在子数组较长时才满足,那么我们可以统计满足 ¬ P 的子数组(R - L + 1),然后用总的子数组数量作差得到答案,也可以直接统计满足 P 的子数组数量,此时应使用 L 来统计。

不过个人还是比较习惯前一种写法,因为比较贴合 “窗口” 的概念。两种写法在时间复杂度上并没有区别。

这里的 “单调性”,准确地来说是 “单峰性”,只不过用单调性更形象一点。因为存在单峰性的数据一般也能用二分法进行处理,那么,能用双指针解决的题目是否也一定能用二分解决呢?这是一个有趣的猜想。

不管怎么说,双指针的时间复杂度更优,而且所需的代码量多数情况下比二分要少,所以我选择双指针。

特判窗口长度为零的情况

滑动窗口类题目中,如果可能出现窗口长度为零的情况,则需要对其特判,否则可能出现匪夷所思的结果。对于统计子数组个数的题目,并不会造成多大影响,然而对于最值统计类题目,就会取到极端值。

滑动窗口不需要前缀和

滑动窗口类题目除非需要查询与窗口端点无关的区间,一般不需要维护前缀和数组,因为区间和都能在窗口移动时进行动态更新。

这一点不仅仅适用于前缀和,如果每次只用到和窗口有关的数据,那么便可以“增量”update/check,不需要重新计算所有数据。

今天做了一天的滑动窗口,大概有二十几道题目,难度分从 1300 到 2500 不等,总体上比做 cf 轻松愉快多了,也没有出现必须看题解才能做出来的题目,不过从题解中学到了许多优化和减少出错的技巧(也就是上面这些东西)。此外,我感觉最重要的是要理解代码每一步的意图,什么时候要特判,什么时候不用,边界条件怎么设置等等,而不是套模板或套以前做过的题目的答案,否则就会写出奇形怪状、难以阅读且出错率高的代码。

std::array 不会自动初始化

以前一直以为它可以自动初始化,今天做题别出心裁把 vector 换成 array,一直报错,打印数据发现全是随机值,这才知道 array 的行为和普通数组一样,如果不提供初值,不会自动初始化!!

定长滑动窗口出窗口

出窗口是 i-len+1,不是 i

左闭右开二分查找

区间上界应取 MAX + 1

需要输出原序号

有些题目涉及排序等导致原输入顺序改变的操作,如果题目要求输出原输入顺序对应的序号,则需要储存这一信息。可以通过在输入数据域中附加序号信息,或使用索引数组实现,如果使用索引数组,所有对数组的操作必须通过索引数组进行。

今天做一道 kruskal 的题目 debug 好久后才发现犯了这个愚蠢的错误……

posted @   ZXPrism  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示