Knuth 单调性:最优 BST 的根不回头
稀疏性:只算 match point 省的是格子:表里多数格子与答案无关,跳过不算。本页的载体换成最优二叉搜索树 (optimal BST),格子一个也省不掉,能省的在每个格子内部:算一个区间的最优代价要在区间里挑根,朴素做法逐一试遍全部候选,而 Knuth 在 1971 年证明最优根的位置单调,每格的枚举窗口被两个已经算好的邻格夹住,全表试根总数从 摊到 。
1 · 问题与朴素填表
给定按键序排好的 个键与访问频率 ,最优二叉搜索树是让总搜索代价 最小的那棵二叉搜索树(根记深度 1)。二叉搜索树只有一条硬性约束:中序遍历等于键序。于是「谁当根」决定一切:选键 作根,键 只能进左子树、 只能进右子树,两侧各自又是同型的子问题。区间 DP 随之而来:
频率和一项与 无关:无论谁当根,区间里每个键都因头上多出这个根而加深一层,整段频率整体加一次; 的空区间代价为 0。表有 个格子,每格枚举 个根,合计 。这个问题在次优查找树 · 静态带权查找的工程折中已经出现过一次,那里用 的贪心绕开它、拿这套 DP 当最优对照;本页不绕,直接压 DP 本身的复杂度。
2 · 最优根的单调性
记 为子问题 的最优根,并列时取最左。Knuth 证明的性质是 [1]:把子数组的一端挪动一格,最优根只会朝同一方向移动,或原地不动。写成不等式:
直觉顺着频率的重心走:右端多接一个键 ,新增的频率全部压在右侧,根被往右拉;左端割掉键 ,剩余部分的重心同样右移。等价的说法是 上三角矩阵的每行、每列都非降。这条观察看着平平无奇,证明却不在直觉的深度上,Erickson 在书里特意标注 nontrivial;本页不复述证明,只交代脉络:Yao 在 1980 年把这类论证抽象成四边形不等式 (quadrangle inequality),划出一整类可以照此加速的 DP [2]。
单调性在引擎里是被机器核对过的事实:checkRootMonotone 逐行逐列扫根表,预设与另外四组手工频率全部通过;另有 20 组随机频率核对朴素与窗口化的代价一致(见 core/engines.test.ts)。
3 · 窗口化枚举与望远镜求和
单调性直接改写算法。按区间长度
从小到大填表,轮到
时,
与
都已在更短的区间里算好,根只需在窗口
里枚举,书中的 ComputeCostAndRoot 写的就是这个循环。
省多少要靠摊还看。固定 ,沿对角线让 从 1 走到 :第 格窗口的上端是 ,而第 格(它的 恰是 )窗口的下端也是 ——相邻窗口首尾相接,共享一个端点。整条对角线的窗口宽度之和于是望远镜式 (telescoping) 地消去中间项,只剩两端之差加格数,不超过 ;用 Erickson 的话说,最外层的每一轮里 单调地从 1 走到 。每条对角线 , 条合计 。
警示 · 伪码假设数组两侧无限延伸。书中 ComputeCostAndRoot 的循环是 for r ← OptRoot[i][j−1] to OptRoot[i+1][j],配套初始化 OptRoot[i][i−1] ← i。照抄实现,
的一圈就会读到
,让
越出合法根区间
一格;数组按合法尺寸开时,cost[r + 1][j] 的行号在
处达到
,引擎首版正是在此读出 undefined。修法是把窗口夹回
,两界与合法区间的交必含最优根,core/obst.ts 里 lo / hi 旁的注释记着这处。
4 · 实测账本
预设频率 、 时最优代价 70。朴素枚举在长度 的对角线上每格试 个根,合计 次;窗口化枚举 66 次,两版填出的代价表与根表逐位一致。逐对角线的账本:
| 试根数 | 合计 | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 朴素 | 8 | 14 | 18 | 20 | 20 | 18 | 14 | 8 | 120 |
| Knuth 窗口 | 8 | 14 | 12 | 9 | 8 | 6 | 5 | 4 | 66 |
头两条对角线分文不省: 时窗口与合法区间重合。省额从 起步,区间越长省得越多, 那格(整段问题)只试了 四个根。总账 66 对 120 只有 1.8 倍,远没有 对 听上去悬殊,因为 太小;但「每条对角线合计 」的形状在这张小表上已经成立——窗口行不随 二次增长,朴素行的 则在中段隆起。
根表还给出一个与加速无关、只有盯着它才会注意的读数:整段问题的最优根是 6 号键(频率 5),不是频率最高的 7 号键(频率 7)。最优根平衡的是两侧的频率总量:6 号作根把总量 30 切成 16 与 9,7 号作根切成 21 与 2,后者把几乎全部重量压进左子树,换来的深度增量抵掉了根上那点频率优势。
5 · 行最小值视角
固定
的一轮里定义矩阵
,
不合法处记
:每次 ComputeCostAndRoot 做的事,就是在
的第
行里找最小值;Knuth 单调性说的则是行最小值的位置随行号非降。一个关于二叉搜索树的技巧至此变成一个关于矩阵的问题:哪类矩阵能不逐行扫描就找出全部行最小值。行最小值:Monge 与 SMAWK 把「非降」加强为 totally monotone,用
的功夫找出全部行最小值,落地:SMAWK 加速一维分段 DP 再把这台机器接回一维分段 DP。
6 · 参考文献
- Knuth, D. E. (1971). Optimum binary search trees. Acta Informatica, 1(1), 14–25.
- Yao, F. F. (1980). Efficient dynamic programming using quadrangle inequalities. Proceedings of the 12th Annual ACM Symposium on Theory of Computing (STOC '80), 429–435.