强烈推荐题解视频
设 f k , i f_{k,i} fk,i表示前 i i i个村庄放了 k k k个邮局的最小代价。
则
f
k
,
i
=
min
j
=
0
i
{
f
k
−
1
,
j
+
w
j
+
1
,
i
}
f_{k,i}=\overset{i}{\underset{j=0}\min}\{f_{k-1,j}+w_{j+1,i}\}
fk,i=j=0mini{fk−1,j+wj+1,i}
表示分配给村庄
[
j
+
1
,
i
]
[j+1,i]
[j+1,i]一个邮局。
设 f 0 , 0 = 0 f_{0,0}=0 f0,0=0,其他状态为正无穷。就是 O ( n 3 ) O(n^3) O(n3)
贡献函数
w
i
,
j
w_{i,j}
wi,j表示给村庄
[
i
,
j
]
[i,j]
[i,j]分配一个邮局的最小贡献。
显然邮局放在区间下标中位数对应的村庄最优。如果有偶数个村庄放在两个中位数处都可以。
因为如果向着远离中位数的地方移动,那么减少的贡献少,而增加的贡献多。
则
w
i
,
j
=
w
i
,
j
−
1
+
a
j
−
a
⌊
i
+
j
2
⌋
w_{i,j}=w_{i,j-1}+a_j-a_{\left\lfloor \frac {i+j}2\right\rfloor}
wi,j=wi,j−1+aj−a⌊2i+j⌋
因为当区间长度由奇数向偶数转移时中位数不变,式子肯定成立。
而区间长度为偶数时可以认为邮局位于右边那个中位数处,中位数也可以认为不变。
打表打出决策单调性,于是可以
O
(
n
2
)
O(n^2)
O(n2):
f
k
,
i
=
min
j
=
p
k
−
1
,
i
p
k
,
i
+
1
{
f
k
−
1
,
j
+
w
j
+
1
,
i
}
f_{k,i}=\overset{p_{k,i+1}}{\underset{j=p_{k-1,i}}\min}\{f_{k-1,j}+w_{j+1,i}\}
fk,i=j=pk−1,iminpk,i+1{fk−1,j+wj+1,i}
#include
#include
#include
using namespace std;
const int N=3e3;
int a[N+5];
int w[N+5][N+5];
int f[305][N+5],p[305][N+5];
int main() {
int n,K;
cin>>n>>K;
for(int i=1; i<=n; i++) cin>>a[i];
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++)
w[i][j]=w[i][j-1]+a[j]-a[i+j>>1];
for(auto&i:f) for(auto&j:i) j=1e9;
f[0][0]=0;
for(int i=1;i<=K;i++) p[i][n+1]=n;注意最优决策点要有初值
for(int k=1; k<=K; k++)
for(int i=n; i; i--)因为在枚举i时用到的i+1的最优决策点,因此要倒序枚举
for(int j=p[k-1][i]; j<=p[k][i+1]; j++)
// for(int j=0; j<=i-1; j++)
if(f[k][i]>f[k-1][j]+w[j+1][i])
f[k][i]=f[k-1][j]+w[j+1][i],p[k][i]=j;
// f[k][i]=min(f[k][i],f[k-1][j]+w[j+1][i]);
cout<<f[K][n]<<endl;
// for(int k=1;k<=K;k++,cout<
// for(int i=1;i<=n;i++,cout<<' ')
// cout<
}
证明决策单调性还是老三样:
首先考虑考虑设状态:
二叉搜索树的一个子树对应有序序列上一个连续的区间,因为中序遍历要递增。所以一眼区间dp。考虑到贡献与层数有关,我们需要把状态写进层数:
设
f
k
,
i
,
j
f_{k,i,j}
fk,i,j表示目前区域根节点深度为
k
k
k,区间为
[
i
,
j
]
[i,j]
[i,j]的最小贡献。
这个dp很明显是
O
(
n
4
)
O(n^4)
O(n4)的,但是十秒钟…也可以过。
但是今天说的是四边形不等式优化dp。
我们首先考虑一下如何把状态优化为
O
(
n
2
)
O(n^2)
O(n2),一般来说牵扯到因为贡献计算而需要增加状态维数的问题,主要有三种计算费用的方法:
因此设
f
i
,
j
f_{i,j}
fi,j表示区间
[
i
,
j
]
[i,j]
[i,j]构成了一颗子树,且假设根深度为
0
0
0时的贡献,则可以写出转移,转移就是枚举选择
k
k
k为根:
f
i
,
j
=
min
k
=
i
j
{
f
i
,
k
−
1
+
f
k
+
1
,
j
+
w
k
(
i
,
j
)
}
f_{i,j}=\overset{j}{\underset{k=i}\min}\{f_{i,k-1}+f_{k+1,j}+w_k(i,j)\}
fi,j=k=iminj{fi,k−1+fk+1,j+wk(i,j)}
设数字用 a a a表示, s s s表示 a a a的前缀和,则其中贡献函数 w k ( i , j ) = s j − s i − 1 + a k w_k(i,j)=s_j-s_{i-1}+a_k wk(i,j)=sj−si−1+ak
时间复杂度 O ( n 3 ) O(n^3) O(n3),足够通过本题了,但是还可以做到更优。
打表打出决策单调性, p i , j − 1 ≤ p i , j ≤ p i + 1 , j p_{i,j-1}\leq p_{i,j}\leq p_{i+1,j} pi,j−1≤pi,j≤pi+1,j,可以优化为 O ( n 2 ) O(n^2) O(n2)
#include
#include
#include
using namespace std;
const int N=250;
int f[N+5][N+5],p[N+5][N+5];
int a[N+5],s[N+5];
int main() {
int n;
for(int i=1;i<=N;i++) p[i][i]=i;
while(cin>>n) {
for(int i=1;i<=n;i++) (cin>>a[i]),s[i]=s[i-1]+a[i];
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) f[i][j]=1e9;
for(int len=2;len<=n;len++)
for(int i=1,j;(j=i+len-1)<=n;i++)
// for(int k=i;k<=j;k++)
for(int k=p[i][j-1];k<=p[i+1][j];k++)
if(f[i][j]>f[i][k-1]+f[k+1][j]+s[j]-s[i-1]-a[k])
f[i][j]=f[i][k-1]+f[k+1][j]+s[j]-s[i-1]-a[k],p[i][j]=k;
cout<<f[1][n]<<endl;
// for(int i=1;i<=n;i++,cout<
// for(int j=1;j<=n;j++,cout<<' ')
// cout<
}
}
因为贡献函数是一个三元函数,所以不知道怎么样满足四边形不等式,所以主要讲究一个倒推。
证明状态函数满足四边形不等式:
设
p
i
,
j
+
1
=
x
,
p
i
+
1
,
j
=
y
p_{i,j+1}=x,p_{i+1,j}=y
pi,j+1=x,pi+1,j=y,则:
{
f
i
,
j
≤
f
i
,
x
−
1
+
f
x
+
1
,
j
+
w
x
(
i
,
j
)
f
i
+
1
,
j
+
1
≤
f
i
+
1
,
y
−
1
+
f
y
+
1
,
j
+
1
+
w
y
(
i
+
1
,
j
+
1
)
f
i
,
x
−
1
+
f
x
+
1
,
j
+
1
+
w
x
(
i
,
j
+
1
)
=
f
i
,
j
+
1
f
i
+
1
,
y
−
1
+
g
y
+
1
,
j
+
w
y
(
i
+
1
,
j
)
=
f
i
+
1
,
j
\left\{
加起来:
f
i
,
j
+
f
i
+
1
,
j
+
1
+
f
x
+
1
,
j
+
1
+
g
y
+
1
,
j
+
w
x
(
i
,
j
+
1
)
+
w
y
(
i
+
1
,
j
)
≤
f
i
,
j
+
1
+
f
i
+
1
,
j
+
f
x
+
1
,
j
+
f
y
+
1
,
j
+
1
+
w
x
(
i
,
j
)
+
w
y
(
i
+
1
,
j
+
1
)
f_{i,j}+f_{i+1,j+1}+f_{x+1,j+1}+g_{y+1,j}+\textcolor{blue}{w_x(i,j+1)+w_y(i+1,j)}\leq f_{i,j+1}+f_{i+1,j}+f_{x+1,j}+f_{y+1,j+1}+\textcolor{red}{w_x(i,j)+w_y(i+1,j+1)}
fi,j+fi+1,j+1+fx+1,j+1+gy+1,j+wx(i,j+1)+wy(i+1,j)≤fi,j+1+fi+1,j+fx+1,j+fy+1,j+1+wx(i,j)+wy(i+1,j+1)
我们希望把贡献函数约掉,因此希望有这样一个不等式:
w
x
(
i
,
j
)
+
w
y
(
i
+
1
,
j
+
1
)
≤
w
x
(
i
,
j
+
1
)
+
w
y
(
i
+
1
,
j
)
\textcolor{red}{w_x(i,j)+w_y(i+1,j+1)} \leq \textcolor{blue}{w_x(i,j+1)+w_y(i+1,j)}
wx(i,j)+wy(i+1,j+1)≤wx(i,j+1)+wy(i+1,j)
这恰为贡献函数的四边形不等式,带入贡献函数的公式会发现显然成立。
剩下的部分就和石子合并的证明一样了,因此状态函数显然满足四边形不等式。
同理,证明决策单调性的部分也和石子合并一模一样,不写了。
所以我们会发现,同一类转移方程能否四边形不等式优化仅仅和 w w w是否满足四边形不等式相关。
于是皆大欢喜。