Skip to main content

区间 DP

Summary

区间 DP(Interval DP)是线性 DP 的扩展,以区间长度为阶段,在小区间最优解的基础上合并出大区间的最优解。设 f(i,j) 为合并区间 i 到 j 的最优值,枚举断点 k 转移,合并代价 w(i,j) 视具体问题而定。以石子合并为例,每次合并相邻两堆、得分为两堆之和,w 取区间元素之和并用前缀和 O(1) 求出,按长度从小到大转移,复杂度 O(n^3);若石子排成环,则把序列复制一倍接在末尾,再对所有长度为 n 的区间取最优。页内附完整实现。

参考资料​

简介​

区间 DP(Interval DP)是线性 DP 的扩展,以区间长度为阶段,在小区间最优解的基础上合并出大区间的最优解。

设 f(i,j)f(i,j) 表示合并区间 [i,j][i,j] 的最优值,枚举断点 kk 转移:

f(i,j)=min⁡i≤k<j{ f(i,k)+f(k+1,j)+w(i,j) }f(i,j)=\min_{i\le k<j}\set{f(i,k)+f(k+1,j)+w(i,j)}

其中 w(i,j)w(i,j) 为合并两段的代价。

以石子合并为例:nn 堆石子排成一列,每次合并相邻两堆,得分为两堆之和,求最小总得分。此时 w(i,j)=∑t=ijatw(i,j)=\sum_{t=i}^j a_t,用前缀和 O(1)O(1) 求出。按区间长度从小到大转移,时间复杂度为 O(n3)O(n^3)。

若石子排成环,把序列复制一倍接在末尾,再对所有长度为 nn 的区间取最优即可。

实现​

523 Bcpp
#include <bits/stdc++.h>
using namespace std;

const int inf=0x3f3f3f3f;
const int N=305;
int a[N],s[N],f[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++)f[i][i]=0;
for(int len=1;len<n;len++)
{
for(int i=1;i+len<=n;i++)
{
int j=i+len;
for(int k=i;k<j;k++)
{
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
cout<<f[1][n]<<'\n';
return 0;
}

例题​

在一个圆形操场的四周摆放 NN 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 22 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 NN 堆石子合并成 11 堆的最小得分和最大得分。