/*
核心:最后一次合并一定是左边连续的一部分和右边连续的一部分进行合并
状态表示:f[i][j]
表示将 i 到 j
合并成一堆的方案的集合,属性 Min
状态计算:
(1) i<j
时,f[i][j]=mini≤k≤j−1f[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)
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N=310;
int n;
int s[N];//存放前缀和
int f[N][N];//f[i][j]表示从第i堆到第j堆合并成一堆,花费的最小代价
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];
//从小到大枚举区间长度,不能从前往后枚举
//len是区间内石子堆的个数
//len从2开始枚举,len为1的时候不用枚举,只有一堆嘛
for(int len=2; len<= n; len++)
for(int i=1;i+len-1<= n; i++)
{
int l=i, r=i+len-1;
f[l][r]=1e8;//因为要算最小值,所以先初始化为正无穷
//在l到r中间,选取可能的分界点,找到合并k两边两堆石子的最小值
for(int k=l;k<r;k++)
//合并l-k 和k+1到r 需要花费的代价是f[l][k]+f[k+1][r]+sum[l-k];
f[l][r]=min(f[l][r], f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
printf("%d\n",f[1][n]);
return 0;
}
02-16
143
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)
03-30
265
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)