什么情况的题目可以使用区间DP?一般有两种情况,第一种是可以将一个大区间的问题拆成若干个子区间合并的问题,还有一种是两个连续的子区间可以进行整合,合并成一个大区间。
基本的解题思路:
-
从小到大枚举区间长度。
-
对于每一个区间长度,枚举该长度的每一个区间。
-
对于每一个区间,枚举所有可能出现的两个子区间的组合。
-
对于每一个子区间的组合,计算出合并所需代价。
-
求出当前区间的最优值。例如f [i] [j]为区间[l,r]的最大价值,则f [i] [j] = max(f[i] [k] + f[k + 1] [r] + cost | l<=k < r)
基本模板:
for(int i= 1;i<=n;i++)
{
cin>>a[i];
f[i][i] = 0;
a[i] +=a[i-1];
}
for(int len = 2;len<=n;len++)
{
for(int i=1;i<=n - len +1;i++)
{
int j=i + len -1;
for(int k = i;k < j;k++)
{
f[i][j] = min(f[i][j],f[i][k] + f[k+1][j] + a[j] - a[i-1]);
}
}
}
cout<<f[1][n]<< '\n';
在实现代码的时候,为啥是这样枚举区间?
for(int i = 1;i<=n;i++){
for(int j = i;j<=n;j++){
for(int k = i ;k < j;k++){
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + sum(i,j));
}
}
}
如上图所示的枚举区间的方式有何问题?如下图解释:
为了加深印象,我们利用一道例题来讲解,这道题有 n 堆石子排成一排,每堆石子有一定的数量。现在我们要将 n 堆石子并成为一堆,每次只能合并相邻的两堆石子,合并的花费为这两堆石子的总数。经过 n−1n−1 次合并后会成为一堆,求总的最小花费。这道题利用上面的模板就可以解决,其中dp[][]数组表示i ~ j组成的数组表示的最i到j堆合成一堆所用的最小花费,故本题输出dp[1][n]即可。
除了这种区间DP,还有一种为环形区间DP,顾名思义,即为区间是首尾相连的环形 ,这种DP方式存在一定的解题步骤:
-
数据的处理方法是将原区间复制一份在后边,总长度x 2。
-
枚举的方法与普通区间DP一致。
-
统计答案时要枚举所有的答案区间,找出最优答案。、
我们同样利用一道例题来加深环形DP的思想,这道题就是处理环形区间的最优分割问题。
解题思路:
循环读取 n 个数值,并将这些数值复制到 v 数组的后半部分,以处理环形的情况。
接下来是动态规划的计算部分。通过两层循环,遍历区间长度 len 从 2 到 n,以及每个长度下的所有区间 [i,j]。在每个区间中,通过枚举分割点 k,更新 f[i][j] 的最优值。具体的更新方式为:f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + v[i] * v[k + 1] * v[j + 1]),其中 f[i][k] 表示区间 [i,k] 的最优解,f[k + 1][j] 表示区间 [k+1,j] 的最优解。
最后,通过遍历更新后的 f 数组,找出环形链表中最大的分割得到的乘积和。具体的做法是通过遍历索引 i,找到 f[i][i+n-1] 中的最大值,即为最终的答案。
基本模板内容:
for(int i=1;i<=n;i++)
{
cin>>v[i];
v[i + n] = v[i];
}
for(int len = 2;len<=n;len++){
for(int i=1;i + len -1 <= n * 2;i++)
{
int j = i + len -1;
for(int k =i;k < j;k++)
{
f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + v[i] * v[k + 1] * v[j + 1]);
}
}
}
int ans = 0;
for(int i = 1;i <= n; i ++)
{
ans = max(ans, f[i][i+n - 1]);
}
在这个解法中,通过将环形结构转化为线性结构,并利用动态规划的思想和状态转移方程,处理了环形区间的最优分割问题。
普通区间DP原题链接:
https://www.lanqiao.cn/problems/1233/learning/?page=1&first_category_id=1&problem_id=1233
环形区间DP原题链接:
https://www.lanqiao.cn/problems/557/learning/?page=1&first_category_id=1&problem_id=557