题意:合并 N 堆石子,每次只能合并相邻的两堆石子,求最小代价
核心:最后一次合并一定是左边连续的一部分和右边连续的一部分进行合并
状态表示:f[i][j]f[i][j] 表示将 ii 到 jj 合并成一堆的方案的集合,属性 Min
状态计算:
(1) i<j 时,f[i][j]=min(i≤k≤j−1)f[i][k]+f[k+1][j]+s[j]−s[i−1]
(2) i=j 时, f[i][i]=0(合并一堆石子代价为 0)
问题答案: f[1][n]
区间 DP 常用模版
所有的区间dp问题,第一维都是枚举区间长度,一般 len = 1 用来初始化
枚举从 len = 2 开始,第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1)
for (int i = 1; i <= n; i++) {
dp[i][i] = 初始值
}
for (int len = 2; len <= n; len++) //区间长度
for (int i = 1; i + len - 1 <= n; i++) { //枚举起点
int j = i + len - 1; //区间终点
for (int k = i; k < j; k++) { //枚举分割点,构造状态转移方程
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
}
}
#include<iostream>
using namespace std;
const int N=310;
int n;
int s[N];
int f[N][N];
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>s[i],s[i]+=s[i-1];
//当区间长度为1时不用合并石子消耗为0 本来就是定义的全局变量
for(int len=2;len<=n;len++){ //先枚举区间长度 再枚举区间左端点
for(int i=1;i+len-1<=n;i++){
int j=i+len-1; //这是右端点
f[i][j]=1e8; //要枚举的是所有k里面的最小值
for(int k=i;k<j;k++){ //枚举k从i到j
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); //加上最后两堆=总和
}
}
}
cout<<f[1][n]<<endl; //所有将1-n合并成一堆的方案的集合
return 0;
}