前言
对于经典区间dp问题,例如石子合并,有以下转移方程:
复杂度 n 3 n^3 n3,可以用四边形优化到 n 2 n^2 n2,GarsiaWachs算法可以做到 n l o g n nlogn nlogn
一、特殊性质
对于上式,可以根据 w ( l , r ) w(l,r) w(l,r)的一些特殊性质,利用决策单调性进行优化
- 区间包含单调性:对于任意 l ≤ l ′ ≤ r ′ ≤ r l\le l'\le r'\le r l≤l′≤r′≤r,均有 w ( l ′ , r ′ ) ≤ w ( l , r ) w(l',r')\le w(l,r) w(l′,r′)≤w(l,r)成立,则称函数 w w w对于区间包含关系具有单调性。
- 四边形不等式:如果对于任意 l 1 ≤ l 2 ≤ r 1 ≤ r 2 l_1\le l_2\le r_1\le r_2 l1≤l2≤r1≤r2,均有 w ( l 1 , r 1 ) + w ( l 2 , r 2 ) ≤ w ( l 1 , r 2 ) + w ( l 2 , r 1 ) w(l_1,r_1)+w(l_2,r_2)\le w(l_1,r_2)+w(l_2,r_1) w(l1,r1)+w(l2,r2)≤w(l1,r2)+w(l2,r1)成立,则称函数 w w w满足四边形不等式(简记为“交叉小于包含”)。若等号永远成立,则称函数 w w w满足四边形恒等式。
二、定理、引理
引理1
若 w ( l , r ) w(l,r) w(l,r)满足区间包含单调性和四边形不等式,则状态 f l , r f_{l,r} fl,r满足四边形不等式。
定理1
若状态
f
f
f满足四边形不等式,记
m
l
,
r
=
m
i
n
{
k
:
f
l
,
r
=
g
k
,
l
,
r
}
m_{l,r}=min\{k:f_{l,r}=g_{k,l,r}\}
ml,r=min{k:fl,r=gk,l,r}表示最优决策点,则有
m
l
,
r
−
1
≤
m
l
,
r
≤
m
l
+
1
,
r
m_{l,r-1}\le m_{l,r}\le m_{l+1,r}
ml,r−1≤ml,r≤ml+1,r 其中
(
l
+
1
<
r
)
(l+1<r)
(l+1<r)
这是优化的关键
证明
核心代码
// C++ Version
for (int len = 2; len <= n; ++len) // 枚举区间长度
for (int l = 1, r = len; r <= n; ++l, ++r) {
// 枚举长度为len的所有区间
f[l][r] = INF;
for (int k = m[l][r - 1]; k <= m[l + 1][r]; ++k)
if (f[l][r] > f[l][k] + f[k + 1][r] + w(l, r)) {
f[l][r] = f[l][k] + f[k + 1][r] + w(l, r); // 更新状态值
m[l][r] = k; // 更新(最小)最优决策点
}
}
例题
石子合并
pass
邮局
题意
数轴上V个村庄,要建P个邮局,使得每个村庄到最近的邮局之间距离之和最小
思路
首先将坐标排序。
定义
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i个村庄放
j
j
j个邮局的前
i
i
i个村庄的最小距离总和,
w
(
i
,
j
)
w(i,j)
w(i,j)表示村庄区间
[
i
,
j
]
[i,j]
[i,j]内放一个村庄时该区间的最小距离总和。
易得
d
p
[
i
]
[
j
]
=
m
i
n
{
d
p
[
k
]
[
j
−
1
]
+
w
(
k
+
1
,
j
)
}
dp[i][j]=min\{dp[k][j-1]+w(k+1,j)\}
dp[i][j]=min{dp[k][j−1]+w(k+1,j)},其中
k
∈
[
0
,
i
)
k\in [0,i)
k∈[0,i)
n
2
n^2
n2预处理
w
w
w:
显然,村庄数为奇数,邮局放中间的村庄,村庄数为偶数,放中间两个村庄之间(包含端点)
则有:
w
[
l
]
[
r
]
=
w
[
l
]
[
r
−
1
]
+
X
[
r
]
−
X
[
l
+
r
2
]
w[l][r]=w[l][r-1]+X[r]-X[\frac{l+r}{2}]
w[l][r]=w[l][r−1]+X[r]−X[2l+r]
若
l
e
n
=
l
+
r
−
1
len=l+r-1
len=l+r−1为奇数,则
l
e
n
−
1
len-1
len−1为偶数,偶数的时候取的是中间两个点都可以,奇数就是中间一个点,所以原本是取中间偏左的点,现在取偏右的点,对区间
[
l
,
r
−
1
]
[l,r-1]
[l,r−1]不影响,只需加上右端点的贡献
若
l
e
n
len
len为偶数,则
l
e
n
−
1
len-1
len−1为奇数,则原先取中间点,现在取中间偏左的点,是一个点,所以对区间
[
l
,
r
−
1
]
[l,r-1]
[l,r−1]也不影响,只需加上右端点的贡献
此时,复杂度为
O
(
n
3
)
O(n^3)
O(n3)
四边形优化
w
w
w是满足四边形不等式的 证明
因此根据上面的定理,
f
f
f也是满足四边形不等式,最优决策记为
m
[
i
]
[
j
]
m[i][j]
m[i][j],则
m
[
i
]
[
j
−
1
]
≤
m
[
i
]
[
j
]
≤
m
[
i
+
1
]
[
j
]
m[i][j-1]\le m[i][j]\le m[i+1][j]
m[i][j−1]≤m[i][j]≤m[i+1][j]
求
f
[
i
]
[
j
]
f[i][j]
f[i][j]时,在
[
m
[
i
]
[
j
−
1
]
,
m
[
i
+
1
]
[
j
]
]
[m[i][j-1],m[i+1][j]]
[m[i][j−1],m[i+1][j]]中找最优解
由于要用到
m
[
i
+
1
]
[
j
]
m[i+1][j]
m[i+1][j],所以倒序求解。
时间复杂度
O
(
P
V
)
O(PV)
O(PV)
代码
#include <bits/stdc++.h>
#pragma optimize GCC(3)
#define show(x) cerr<<#x<<" : "<<x<<endl;
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 3e3 + 5;
int x[maxn];
int w[maxn][maxn];
int f[maxn][maxn], m[maxn][maxn];
void solve() {
int V, P; cin >> V >> P;
for (int i = 1; i <= V; i++)cin >> x[i];
sort(x + 1, x + V + 1);
//预处理w
for (int i = 1; i <= V; i++)
for (int j = i + 1; j <= V; j++)
w[i][j] = w[i][j - 1] + x[j] - x[(i + j) / 2];
memset(f, inf, sizeof(f));
f[0][0] = 0;
for (int j = 1; j <= P; j++) {
m[V + 1][j] = V;
for (int i = V; i >= 1; i--) {
for (int k = m[i][j - 1]; k <= m[i + 1][j]; k++) {
if (f[i][j] > f[k][j - 1] + w[k + 1][i]) {
f[i][j] = f[k][j - 1] + w[k + 1][i];
m[i][j] = k;
}
}
}
}
cout << f[V][P];
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//int t; cin >> t; while (t--)
solve();
}