专栏文章
学习笔记——四边形不等式优化 DP
算法·理论参与者 49已保存评论 55
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 55 条
- 当前快照
- 1 份
- 快照标识符
- @mhz5t7yo
- 此快照首次捕获于
- 2025/11/15 01:56 3 个月前
- 此快照最后确认于
- 2025/11/29 05:24 3 个月前
0. 前言
你打开了一道题,你发现是个 DP,你写出了转移方程 ,你发现 可以 计算。你兴致勃勃地写出了代码,却得到了满面 TLE。一看数据范围,。
你点开题解,发现一种神奇的对于 的特殊性质进行优化的方法。这就是四边形不等式优化 DP。
1. 定义和性质
如果对于二维序列 中任意四个整数 都有 ,则称 满足足四边形不等式关系。
性质
若对于任意两个整数 ,都有 ,那么 满足四边形不等式。
证明:
取 ,那么由上文的前提可得,我们令 ,则有 ,即 。
两不等式相加,即 ,整理可得 。
接着取 ,可继续按照上面的做法推广,最终可得 。
可以发现我们可以取 等等,都可以证出类似的结论。即对于任意 ,都有 。接着我们只需要把 按照类似的方法推广到 即可。
取 ,则有 。
两不等式相加,即 ,整理可得 。
取 ,继续按照类似方法,有 。
可无限取 ,即对于任意 ,都有 。
至此,我们得以证明。
上述的结论通常用于证明 满足四边形不等式,与下文的 DP 优化关联不大。
PS:为什么叫四边形不等式?

对于任意四边形,显然有对角线的和 两对边之和,如图,。证明方式为三角形两边之和大于第三边。
2. 优化 DP
考虑一类决策性 DP,其转移方程形如 。这类转移方程通常是类似这样的题目: 个数,要分成若干连续段,定义每一段的价值,要求最小化每段价值之和。这时我们设 表示前 个数分段的最小价值和,那么枚举 即枚举 属于哪一段, 则是这一段的价值。
首先要引入什么是决策,我们设 表示表示对于 而言,我们枚举的 中,使得 取到最值(在上文的转移方程中即最小值)的 ,则我们称 为 的决策点。
对于这一类问题,显然有朴素的 计算方式。对于部分题目,如果 满足四边形不等式性质,我们可以考虑用下文的方式优化。
性质 1
若 满足四边形不等式,则 的决策点具有单调性。
性质 2
若有 使得对于某个 ,选取 的转移比 要优,则对于 ,取 也比 优。容易发现 为 的决策点时与性质 等价,即性质 是性质 的特殊情况。
特别注意需要满足 。
证明:
由于 比 对 的贡献更优,所以有:。因为 满足四边形不等式关系,则有 ,移项变为 。
将最后一个不等式和第一个不等式相加,有 。
证毕。
如何优化 DP
我们可以设初始时每个 的决策点都为 ,然后从前往后,依次用决策点算出 并尝试用 作为新的决策点更改其他点。
由于性质 成立,所以如果 可以更新 ,那么一定可以更新 到 。所以 能更新的点一定是末尾一段。假设能更新的是 ,我们的目标就是找到 。容易发现是可以二分的。
我们考虑维护一个队列,每个点维护三个数 ,表示 每个点的当前最优决策都为 。这个队列是对于 的单调队列。我们对于 ,先从后往前找到 所在的 ,然后在 中二分 的位置并更新队列即可。
当然也有一些其他实现方法,例如分治维护,复杂度也是 。
特别注意:
上文的做法对于四边形不等式是正确的,但对于普通的决策单调性是错误的。
对于普通的决策单调性 DP,只能保证最终的决策点是单调的,换句话说,只能保证性质 成立而不能保证性质 成立。
我们在维护单调队列进行更新的部分时,不能保证能找到 。 能更新的不一定是末尾一段。
我们可以通过几张图来更好地理解(下文的图中,横轴表示 ,纵轴表示 )。
这张图给出的是 的一个例子。

假如我们只证明了决策单调性,那么对于 ,图可能是这样:

容易发现决策点也是不递减的,但是对于 , 比 优,但是对于 , 却比 优。
这在证明 满足四边形不等时式后是不成立的,因为其不满足性质 。如果证明后,那么图可能是这样的:

容易看出,对于 的图,纵轴的点的相对关系是不变的。
所以可以看出,四边形不等式得到的结论强于决策单调性的。
注意,四边形不等式是证明 单调性的一种充分不必要方式。即证明 满足四边形不等式可以推出 单调,但 单调不一定要求 满足四边形不等式。
例题
P3195 [HNOI2008]玩具装箱
考虑一个 的 dp。
设 表示前 个玩具的最小总费用, 表示前 个玩具的长度和,即前缀和。
有转移方程:。在这题中前文的 即后面的 ,我们只需要证明 满足四边形不等式即可。
至于如何证明,通常可以考虑在本地 枚举 ,观察对于小数据是否满足,比较严谨的数学证明暂时略去。这里严谨的证明方式需要使用到文章开头的性质。
因为满足四边形不等式,把上述的优化 DP 方案加以修改即可。
代码:
CPP#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
#define int long long
const int N = 5e4 + 5;
int a[N], n, l, dp[N], sum[N], op[N];
struct Node
{
int c, l, r;
}q[N];
inline int val(int i, int j) // 计算对于 j 而言,决策为 i 的答案
{
int s = sum[j] - sum[i] + j - i - 1 - l;
return dp[i] + s * s; // 即上文的 dp[i] + v
}
int hh, tt;
void insert(int x) // 用 x 更新决策方案
{
int pos = n + 1; // 临界点
while (hh <= tt && val(q[tt].c, q[tt].l) >= val(x, q[tt].l)) pos = q[tt--].l; // 找到用 x 作为决策点能更新到的队列,因为满足四边形不等式,所以更新的肯定是队列末尾的连续一段
if (hh <= tt && val(q[tt].c, q[tt].r) >= val(x, q[tt].r)) // 如果我们找到了这个区间
{
int l = q[tt].l, r = q[tt].r;
while (l + 1 < r) // 二分 l 到 r 中哪个点是临界点
{
int mid = l + r >> 1;
if (val(x, mid) <= val(q[tt].c, mid)) r = mid;
else l = mid;
}
q[tt].r = r - 1; // 更新队列和 pos
pos = r;
}
if (pos != n + 1) q[++tt] = { x, pos, n }; // 如果可以更新,就在后面更新
}
signed main()
{
scanf("%lld%lld", &n, &l);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
hh = tt = 0;
q[0] = { 0, 1, n }; // 初始化,1 到 n 一开始的最优决策都是 0
for (int i = 1; i <= n; i++)
{
dp[i] = val(q[hh].c, i); // 计算已经求出最优决策点对于现在点的 dp 值
op[i] = q[hh].c; // 我们求出的 op[i] 表示每个点的最优决策
if (q[hh].r == i) hh++; // 如果到达了队列的某一项末尾,则决策点改变,即 hh++
q[hh].l = i + 1; // 重新计算 l
insert(i); // 用 i 作为更新决策
}
printf("%lld\n", dp[n]);
return 0;
}
由于四边形不等式优化 DP 的题目不多,而且大部分都较为类似,所以不增加更多例题了。
总结
在做一些决策性 DP 的问题时,如果 满足四边形不等式,我们可以考虑四边形不等式优化 DP,复杂度通常会由 降至 。如何证明 满足四边形不等式?具体的数学证明我们通常不会在做题时证明,有时可以写 的暴力,对于小数据验证四边形不等式,有时也可以感性理解。四边形不等式本身的性质有许多需要严谨证明的地方,建议自己再推一次。
除此之外,四边形不等式还可以优化决策型区间 DP,例如下面提到的再探石子合并。
笔者水平有限,若有错误或建议欢迎提出,感谢。
相关推荐
评论
共 55 条评论,欢迎与作者交流。
正在加载评论...