区间DP
区间DP
1.区间DP简介
区间型动态规划是线性动态规划的拓展,它将区间长度
作为阶段,长区间的答案与短区间有关。
在求解长区间答案前需先将短区间答案求出。
区间型动态规划的典型应用有石子合并、乘积最大等。
2.经典例题:石子合并
题目描述
在一个操场上一排地摆放着 \(N\) 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 \(2\) 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
计算出将 \(N\) 堆石子合并成一堆的最小得分。
输入
第一行为一个正整数 \(N\);
以下 N 行,每行一个正整数,分别表示第 \(i\) 堆石子的个数 \((1≤i≤N)\)。
输出
一个正整数,即最小得分。
输入样例
7
13
7
8
16
21
4
18
输出样例
239
数据范围
\(2\leq N\leq 100\)
\(0\leq\) 每堆石子的数量 \(\leq 10000\)
注:本题来自信息学奥赛一本通第1274题
3.经典例题思路
还是一样,我们先来设 \(f\) 数组:
所有将第 \(i\) 堆石子到第 \(j\) 堆石子合并成一堆的最小合并方式
在状态计算方面,我们可以把每一堆石子分成两个区间。
每次都枚举一个 \(k\),左区间有 \(1\) 个元素,右区间就有 \(k-1\) 个元素;左区间有 \(2\) 个元素,右区间就有 \(k-2\) 个元素,以此类推……
通过这一点,我们就可以用左区间的最小得分加上右区间的最小得分,再加上合并左区间和右区间的得分来推出每个区间的最小值。
所以,状态转移方程为:
\[f[i][j]=min(f[i][k]+f[k+1][j]+s[j]-s[i-1])
\]
注1:\(k\) 是从 \(i\) 枚举到 \(j-1\) 的
注2:此处 \(s\) 数组是前缀和数组,即 \(s[i]\) 为 开始元素一直加到第 \(i\) 个元素的值
4.经典例题代码(注释已全部标出)
#include<iostream>
using namespace std;
const int N=305;
int n;
int s[N];
int f[N][N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=1;i<=n;i++) s[i]+=s[i-1];//求前缀和
for(int len=2;len<=n;len++)//要枚举的区间长度,因为len=1的情况结果为0,所以代码中不用体现
for(int i=1;i+len-1<=n;i++){//左区间位置
int l=i,r=i+len-1;//区间的左端和右端
f[l][r]=1e8;//每次都要初始化,要不然就全是0
for(int k=l;k<r;k++)
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);//状态转移方程
}
printf("%d",f[1][n]);
return 0;
}
完awa~
如果觉得还行,就点个赞吧,您的支持对我来说很重要,谢谢。