力扣周赛最考验人的地方,不是会不会某个算法,而是能不能在有限时间内正确判断“这题该用什么思路”。第 514 场周赛,我拿到题目之后没有急着写代码,而是先把四道题都按分治的角度过了一遍:能不能对半拆,拆完能不能直接合并,跨过中点的贡献要不要单独补算。这里不刻意还原原题逐字描述,赛后官方题解会讲得更细;我更想聊的是分治这条线上,周赛题常见的几种形态,以及我平时怎么判断、怎么写、怎么排错。不管你是刚打完 514,还是回头补 430,这套判断流程都能直接用。
1. 分治在周赛里,往往不是“一眼分治”
1.1 分治的本质是“拆得开、合得起”
分治的流程表面上只有三步:分解、解决、合并。难点从来不在“分解”,而在“合并”。很多人在周赛里卡住,不是因为想不到要把数组对半分,而是不知道左右两半的结果怎么合成一个答案,或者忽略了跨过中点的贡献。
举个例子,统计数组里满足i < j且nums[i] > 2 * nums[j]的下标对数。表面看,这不是二叉树,也不是快速幂,但它就是典型的分治题:把数组从中间切开,左边内部的下标对、右边内部的下标对递归处理;跨过中点的下标对,需要在合并阶段用双指针或有序性补算。能说清“左、右、跨中点”这三类贡献分别怎么算,题目就已经解了一半。
所以我的第一个建议是:遇到疑似分治的题,先逼自己说出这句话——“这道题的子问题是什么,合并时哪部分贡献是新的。”说不出来,说明还没真懂题意,直接写代码大概率是错的。
1.2 周赛里最常见的几种分治外壳
分治不会直接写在题目上,它往往套着不同的外壳。周赛里常见的有这几种:
| 外壳 | 分治内核 | 周赛常见位置 |
|---|---|---|
| 二分查找、二分答案 | 每次把问题规模折半,快速排除一半空间 | 第 2、3 题 |
| 下标对 / 子数组统计 | 左右区间内部递归,跨中点贡献单独统计 | 第 3、4 题 |
| 二叉树的递归处理 | 左右子树天然拆分,结果合并到父节点 | 第 2、3 题 |
| 分治优化动态规划 | 用分治加速部分状态转移 | 第 4 题 |
这里要说明一下,二分查找严格来说不算分治,但它的“规模减半 + 只处理需要的那一半”的核心思路和分治同源。周赛里经常出现先二分答案、再在 check 函数里用双指针或贪心判断的题,这种题真正难的是 check,不是二分本身。
1.3 先能说出“子问题是什么”,再谈算法
写代码之前,我习惯先给每道题写一句话,记录子问题:
- 子问题是“左半段内部的答案”;
- 子问题是“右半段内部的答案”;
- 新增的是“跨过中点的答案”。
如果第三个问题回答不出来,就退回暴力思路,看暴力枚举里哪些比较是多余的。能去掉重复枚举,合并逻辑自然就浮现了。这个动作看着简单,但能避免你在错误的算法方向上走太远。
2. 判断一道题该不该用分治
2.1 连问自己三个问题
比赛里时间紧,我一般用三个问题快速判断:
- 把输入对半拆开,两半各自能不能独立算出一个局部答案?
- 全局答案是不是“左半答案 + 右半答案 + 跨中点答案”的组合?
- 跨中点的答案能不能用 O(n) 或 O(n log n) 的时间合并出来?
三个都满足,分治基本可行。第一个不满足,说明问题有全局依赖,可能更适合贪心、图遍历、最短路或差分。第二个不满足,说明答案不是