leetcode3658 穿回初中的我,除了辣条什么都能优化

穿回初中的我,除了辣条什么都能优化

事情是这样的——
那天我加班到凌晨三点,正和一段祖传的“屎山代码”搏斗,突然眼前一黑,晕了过去。

再睁眼时,鼻腔里满是粉笔灰和劣质圆珠笔的味道。阳光被窗外的梧桐叶剪成碎片,洒在桌面上。黑板左上角写着 “等差数列求和” 六个大字,右下角则是一颗锃亮的地中海,在阳光里反射出近似 LED 指示灯的光泽。

“王老师……”我下意识嘟囔。

旁边一个瘦猴同桌用胳膊肘狠狠捅了我一下:
“别睡了!老王要提问了!你刚才都打呼了!”

我低头一看,自己穿着蓝白校服,桌上摊着七年级数学课本,扉页歪歪扭扭写着“初一(3)班 李码”。

完了,穿越了。
而且穿越的时机极其不巧——王老师正推着眼镜,目光像指针一样指向我。

“李码!你上来,把这道题演示一下。我看你刚才睡觉的样子,肯定在梦里把题都做完了吧!”

全班哄笑。 我踩着虚浮的脚步走上讲台,心里只有一个念头:撤回键在哪?Ctrl+Z!Ctrl+Z!


黑板上的题目,居然还很眼熟:

给你一个正整数 n,n不超过 1000 记: S奇 = 最小的 n 个正奇数的和 S偶 = 最小的 n 个正偶数的和

求 S奇 与 S偶 的最大公约数。

我心里一惊,这不是 LeetCode 3658 吗?一个程序员刷到这题,就像在新手村门口看到一只走地鸡。

我下意识想敲 import math,结果发现手里只有一支短得可怜的粉笔头;又习惯性想按 Tab 自动补全,结果只是两根手指在空中痉挛了两下——等等,我的 IDE 呢,我的 Copilot 呢?

同桌在下面小声嘀咕:“他在干嘛……搓大招吗?”

王老师皱眉:“李码,你手抽筋了?”

我回过神来,深吸一口气。 好吧,这破穿越系统连个新手礼包都不发,但至少我的大脑还装着九年义务教育加三年算法刷题攒下的内力。


我抓起粉笔,在黑板上端端正正地写:

第一步:求 S奇

最小的 n 个正奇数依次为:1,3,5,7,…,2n-1。 观察可知,这是一个等差数列,首项为 1,末项为 2n-1,项数为 n。 由等差数列求和公式,得: S奇 = (首项 + 末项)× 项数 ÷ 2 = (1 + 2n - 1) × n ÷ 2 = 2n × n ÷ 2 =

写完我手腕一抖,差点在后面戳了个分号——“Unnecessary semicolon” 我在心里默默 lint 了自己一把。

**第二步:求 S偶 **

最小的 n 个正偶数依次为:2,4,6,…,2n。 该数列为首项 2、末项 2n、项数 n 的等差数列。 同理,由求和公式得: S偶 = (2 + 2n) × n ÷ 2 = n(n+1)

底下已经有同学开始倒吸凉气——毕竟初一生看字母运算,就像我第一次看没有类型注解的 Python 一样头皮发麻。


**第三步:求 GCD **

我把粉笔往黑板上一敲,职业病当场发作。

“好,现在问题变成了求 n² 和 n(n+1) 的最大公约数。如果暴力破解——先循环求和,再调用 gcd,复杂度 O(n),对于 n=1000 来说倒也够,但我们是工程师,要追求 O(1) 的极致优雅。”

王老师一脸懵:“欧……欧什么?”

我假装没听见,继续往下写:

由欧几里得算法可知:gcd(a, b) = gcd(a, b - a) 令 a = n²,b = n(n+1) = n² + n 则 gcd(n², n² + n) = gcd(n², n) 而 n 能整除 n²,故 gcd(n², n) = n

粉笔在 n 上狠狠画了个圈。

答案是 n。不管 n 是 1 还是 1000,直接返回 n,别的啥也不用干。”

为了强化视觉效果,我还简单画了个流程图:开始 → 拿到 n → 直接输出 n → 结束

底下沉默了整整三秒,安静得能听见前排学霸同学转笔掉地上的声音。

王老师推了推眼镜,盯着黑板上的 gcd(n², n²+n) = n,突然问:
“你这‘欧几里得算法’……是哪本参考书上的?怎么我讲等差数列求和,你直接跳到数论了?”

我把粉笔头往盒子里一丢,拍了拍满手的白灰,下意识手插裤兜——没摸到手机,倒是掏出了两包辣条。 “王老师,这是一个程序员的基本素养。您想想——哪个硬核玩家打 BOSS 还一刀一刀平砍的?找机制漏洞,一招秒杀。”

同桌在后排张大了嘴:“李码,你是不是偷偷补课了……”


后来我确实因为“上课讲话太飘且公然携带零食”被罚站了十分钟。

我靠在走廊墙上,嘴里叼着根辣条,心情比连过三道 Hard 题还爽。窗外阳光正好,操场上有班级在跑圈,微风吹过来全是初夏的味道,混着粉笔灰和食堂中午的炸鸡排味儿——这配方,二十年后花多少钱都调不出来。

同桌趁老师写板书,从后门探出半个脑袋:“李码!你刚才说的那个欧欧欧到底是什么?你是在装逼还是真会啊?”

“欧几里得算法,求最大公约数用的,回头教你,包教包会。”

他眼睛一亮,然后又皱眉:“那你刚才在空中戳来戳去那个手势是啥意思?”

“那叫 Tab 自动补全,是一种……呃,失传的手艺。”

他似懂非懂地缩回去了。走廊那头,隔壁班的英语课代表抱着一摞作业本路过,她瞥了我一眼——大概是在想,这人怎么罚站罚得满脸笑嘻嘻的。


彩蛋:

这道题本质就是数学化简:

  • sumOdd = 1 + 3 + … + (2n-1) =
  • sumEven = 2 + 4 + … + 2n = n(n+1)
  • gcd(n², n(n+1)) = gcd(n², n) = n

所以无论 n 多大(在题目的 1 ≤ n ≤ 1000 内),答案都是 n。 复杂度 O(1),额外空间 O(1)。

对应代码已经简单到了一种行为艺术的程度:

def gcdOfOddEvenSums(n: int) -> int:
	return n

如果要整花活,也可以用 math.gcd 或手搓一个gcd 验证一下:

# from math import gcd
def gcd(a: int, b: int):
    if b == 0:
        return a
    return gcd(b, a%b)

def gcdOfOddEvenSums(n: int) -> int:
    sum_odd = n * n
    sum_even = n * (n + 1)
    return gcd(sum_odd, sum_even) 

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。