浅谈算法竞赛中C++的常数优化

本人第一篇博客,就决定从这个话题写起了。

在正文开始之前,请注意:受限于作者的水平,本文只适用于初级的(NOIP-)算法竞赛

以及,这里给出几个前提:

  • 算法竞赛要考察算法!(👋😭👋)
  • NOI大纲2025中明确注明要减少对算法常数的考察。
  • 本文只面向算法竞赛的赛时代码
  • 在赛场上,你的时间是有限的
  • 在NOI系列赛事中,-O2-std=c++14 已全面开启。
  • 算法竞赛中,一切为了分数

好了,下面是正文。

一些建议写的优化

大量定模数取模运算时的优化

如:题目要求你把结果对某个模数(如1e9+7)取模时。

这时,我建议你把模数设定为一个常量,即const int mod = 1e9+7,而不是直接把它设置成一个变量。

其中具体的门道我也说不太清,总之,模数写成常量形式后会更方便编译器去优化。

不需要动脑子就可以实现的输入输出优化

在开始编写你的代码前,建议把这几行代码写上去:
#define endl '\n'
ios::sync_with_stdio(false); cin.tie(nullptr);
然后,在后面的输入输出中,使用C++风格的输入输出(cin, cout)。

实际上,这些工作减少的代码常数已经足够让你在NOIP及以下的题目中不被卡常数了,下面是解释:

#define endl '\n'的作用是:将代码中所有的 endl 替换成 '\n'
究其原因,是endl的常数实在是太大了。这点或许读者有所耳闻,但不知道具体大到什么程度,这里给出我在本地测试的数据。

在AMD Ryzen 7 8845HS w,-O2与-std=c++14环境下输出1e6个1后换行:

\ endl '\n' printf下的'\n'
synced 1884ms 79ms 86ms
unsynced 1835ms 65ms(本文推荐写法) 79ms(不生效)

'\n'的好处已经不言而喻了,但或许这时会有人说:“你这里synced和unsynced的优化也不明显呀,我为什么要费劲写这一行呢?”

那是因为,关闭同步流(即ios::sync_with_stdio(false); cin.tie(nullptr);)的优化主要体现在输入上,下面给出测试数据:

在AMD Ryzen 7 8845HS w,-O2与-std=c++14环境下读入1e6个1:

\ cin scanf
synced 240ms 200ms
unsynced 84ms(本文推荐写法) 204ms(不生效)

这下,优势就显现出来了。

一些不建议写的优化

快读与快写

当然,这个时候或许有人要说:“那我写快读快写呢?”

这里,就不得不强调上面的那几个前提了。

先不说你能否在赛场上写出正确的快读快写,就算能写出来,10ms左右的优化是几乎不可能给你带来更多的分数的。

当然,我不否认在平常训练时学习快读快写会加深你对C++的理解,但本文面向的是赛时代码,所以我本人仍强烈建议:

抛弃快读快写,拥抱解除同步流的C++风格io。

火车头(#pragma ......)

NOI系列赛事不让用

inline与register

-O2 后会被忽略。

简单的代数式优化

(如:将(i+3)*3写成i*3+9,将i/2写成i>>1等)
-O2 后编译器会帮你。

总结

在使用了上面这些优化方法后带来的效果已经足够你应付NOIP及以下的算法竞赛了,但如果你进入到了省选甚至NOI的赛场,请学习更深奥的常数优化方法,这里我推荐洛谷 深入浅出程序设计竞赛 提高篇中常数优化章节,里面涉及了很多进阶的常数优化算法,本文的灵感也来源于此书。

posted @ 2025-07-18 12:49  Misaka2298  阅读(50)  评论(0)    收藏  举报