四边形不等式优化:从石子合并到划分 DP
石子合并大概是很多人学区间动态规划时遇到的第一道例题。题目很简单:一排石子,每次合并相邻两堆,代价是合并后的石子总数,问把所有石子合并成一堆的最小总代价。经典解法是设 \(\text{dp}[i][j]\) 为合并区间 \([i,j]\) 的最小代价,前缀和求出区间和 \(\text{cost}(i,j)\),转移为
三层循环 \(i,j,k\) 写下来,时间复杂度是 \(O(n^3)\)。当规模稍大,这个复杂度基本就不现实了。
有人发现,在石子合并这个看似平平无奇的代价函数下面,最优的那个切分点 \(k\) 并不是在每个区间里乱窜的。如果把每次找到的最优切分点记作 \(\text{opt}[i][j]\),把这些点打印出来,会看到一个很强的规律:
也就是说,随着区间右端点右移,最优切分点不会向左退;随着左端点右移,最优切分点也不会向左退。这被称作决策单调性。利用它,我们可以把 \(k\) 的枚举范围从整个 \([i,j)\) 压缩到 \([\text{opt}[i][j-1], \text{opt}[i+1][j]]\),再和合法范围 \([i,j-1]\) 取交。这样就能把石子合并从 \(O(n^3)\) 优化到 \(O(n^2)\)。这就是 Knuth 优化,也叫做“四边形不等式优化”。
四边形不等式是什么
上面说的规律要想严格成立,代价函数 \(\text{cost}\) 通常需要满足两个条件。对任意 \(a \le b \le c \le d\):
- 区间包含单调性:\(\text{cost}(b,c) \le \text{cost}(a,d)\),即小区间的代价不大于包含它的大区间。
- 四边形不等式:\(\text{cost}(a,c) + \text{cost}(b,d) \le \text{cost}(a,d) + \text{cost}(b,c)\),交叉区间代价之和不大于包含区间代价之和。
对石子合并来说,\(\text{cost}(i,j)\) 就是区间和,区间包含单调性显然成立;而四边形不等式对区间和也成立,且恰好取等号:
这两个性质为什么重要,是因为 \(\text{cost}\) 上的四边形不等式会“传染”给 DP 数组。若 \(\text{cost}\) 满足上述两条性质,那么可以证明 \(\text{dp}[i][j]\) 本身也满足四边形不等式,进而推出我们想要的决策单调性。
为了避免多个最优切分点时产生歧义,下面默认 \(\text{opt}[i][j]\) 表示最左侧的最优切分点。代码里从小到大枚举 \(k\),并且只在严格更优时更新,也正好符合这个定义。证明的核心套路是反证法。
假设 \(\text{opt}[i][j] < \text{opt}[i][j-1]\),令 \(x = \text{opt}[i][j]\),\(y = \text{opt}[i][j-1]\),于是 \(x<y\)。由于 \(\text{cost}(i,j)\) 在同一个状态里和切分点无关,比较两个切分点时可以把它消掉。由最优性有
两式相加,抵消掉 \(\text{dp}[i][x]\) 和 \(\text{dp}[i][y]\),得到
注意到 \(x<y\),所以 \(x+1 \le y+1 \le j-1 \le j\)。而 \(dp\) 的四边形不等式给出的是
两边合起来说明这两个候选切分点在相关状态上只能处在相等的边界情形。此时由于我们约定 \(\text{opt}[i][j]\) 取最左最优点,与 \(x<y\) 且 \(y\) 仍可作为同样优的更右切分点这一假设不相容。因此 \(\text{opt}[i][j] \ge \text{opt}[i][j-1]\) 必须成立。
右半部分的证明也可以用对称的方式完成。过程中真正依赖的是 dp 自身的四边形不等式,而 dp 的四边形不等式又来源于 cost 的区间包含单调性和四边形不等式——这就是“传染”的含义。
石子合并的 O(n²) 实现
决策单调性到手之后,代码的改动小得让人意外。原本我们习惯按区间长度从小往大推,内层枚举 \(k\) 从 \(i\) 到 \(j-1\)。现在只需要把 \(k\) 的起止范围换成 \(\text{opt}[i][j-1]\) 和 \(\text{opt}[i+1][j]\),并额外记录一下每个状态的最优决策点。初始时对于长度为 1 的区间让 \(\text{opt}[i][i]=i\) 占位。
需要注意的是,转移中的切分点必须满足 \(i \le k < j\)。因此实际枚举范围还要和合法区间 \([i,j-1]\) 取交,否则长度为 \(2\) 的区间可能会枚举到非法的 \(k=j\)。
for i in range(1, n + 1):
dp[i][i] = 0
opt[i][i] = i
for length in range(2, n + 1):
for i in range(1, n - length + 2):
j = i + length - 1
dp[i][j] = INF
# 决策单调性给出的范围,再和合法切分范围 [i, j - 1] 取交
left = max(opt[i][j - 1], i)
right = min(opt[i + 1][j], j - 1)
for k in range(left, right + 1):
val = dp[i][k] + dp[k + 1][j] + pref[j] - pref[i - 1]
if val < dp[i][j]:
dp[i][j] = val
opt[i][j] = k
可以证明,对于每个固定的区间长度,所有 \(k\) 的枚举量加起来是 \(O(n)\),所以总复杂度为 \(O(n^2)\)。
环形石子合并也可以使用这个思路。常见做法是破环成链,把数组复制一遍变成长度 \(2n\),只计算长度不超过 \(n\) 的区间。最后答案是在所有长度为 \(n\) 的区间中取最小值。优化逻辑不变,但边界和数组大小需要额外小心。
更一般的模型:划分 DP 与邮局选址
石子合并属于“合并型”区间 DP,转移形如
Knuth 优化依赖的是
这类上下界非常强,因此能把区间 DP 从 \(O(n^3)\) 优化到 \(O(n^2)\)。
四边形不等式还会出现在另一大类问题里,也就是划分 DP。典型代表是 IOI 2000 的邮局选址:一条直线上有 \(n\) 个村庄,位置分别为 \(x_1 \le x_2 \le \cdots \le x_n\),要建 \(p\) 个邮局,使得所有村庄到最近邮局的距离之和最小。
朴素做法设 \(\text{dp}[i][j]\) 表示前 \(j\) 个村庄建 \(i\) 个邮局的最小总距离,转移为
其中 \(w(L,R)\) 是在区间 \([L,R]\) 内建一个邮局时的最小距离和。单个邮局的最优选址是这些村庄的中位数位置,因此
其中 \(\text{mid} = \left\lfloor (L+R)/2 \right\rfloor\)。朴素枚举 \(i,j,k\) 的复杂度是 \(O(pn^2)\),在 \(n\) 和 \(p\) 较大时同样吃力。
邮局选址虽然也和四边形不等式有关,但它通常使用的是分治优化,而不是前面石子合并那套 Knuth 优化。在固定层数 \(i\) 时,随着 \(j\) 增大,最优断点不会向左移动,也就是
有了这个单调性,就可以用分治递归计算同一层的所有状态。每次计算中点 \(\text{mid}\),只在给定的候选区间 \([\text{opt}_l,\text{opt}_r]\) 中枚举 \(k\),然后把最优点传给左右两半。
# dp[0][0] = 0
# dp[0][j] = INF, j > 0
# w(L, R) 可以 O(n^2) 预处理
def solve(layer, left, right, opt_left, opt_right):
if left > right:
return
mid = (left + right) // 2
best_val = INF
best_k = -1
upper = min(opt_right, mid - 1)
for k in range(opt_left, upper + 1):
val = dp[layer - 1][k] + w[k + 1][mid]
if val < best_val:
best_val = val
best_k = k
dp[layer][mid] = best_val
opt[layer][mid] = best_k
solve(layer, left, mid - 1, opt_left, best_k)
solve(layer, mid + 1, right, best_k, opt_right)
for layer in range(1, p + 1):
solve(layer, layer, n, layer - 1, n - 1)
这套写法把每一层的 \(O(n^2)\) 转移降到 \(O(n \log n)\),整体复杂度为 \(O(pn \log n)\)。
石子合并和邮局选址的共同点是都能从“代价函数的结构”推出“最优决策点的单调性”;区别在于它们的 DP 形态不同,使用的优化模板也不同。石子合并用的是 Knuth 优化,邮局选址更常见的是分治优化。
何时能用,何时不能
四边形不等式相关的优化虽然强,但前提很明确:代价函数和 DP 形态必须一起满足相应的条件。实际中常见的“好”代价函数包括:区间和、某些关于区间长度的凸函数、中位数绝对距离和等。
对于取 \(\max\) 的问题,决策单调性有时依然成立,但不等号方向可能反过来,也可能需要重新定义最优决策点的选取规则,不能直接照搬取 \(\min\) 的结论。
也有不少看起来很像的问题其实不满足条件。一旦 \(\text{cost}\) 没有四边形不等式,或者 DP 的状态结构并不支持这种“传染”,最优决策点就真的可能跳跃。此时强行压缩枚举范围会直接导致错误。
遇到一个 \(O(n^3)\) 的区间 DP 时,可以先做三件事:
- 看它是不是石子合并、最优二叉搜索树这类合并型区间 DP。
- 检查 \(\text{cost}\) 是否满足区间包含单调性和四边形不等式。
- 明确最优决策点的单调形式到底是 Knuth 优化里的双边界,还是划分 DP 里的单层单调。
若三者都对,再动手改循环顺序;若不成立,就应该另寻他路,比如分治优化、斜率优化、单调队列优化或其他数据结构优化。

浙公网安备 33010602011771号