石子合并(弱化版)
洛谷题面
题目大意
设有 \(N(N \le 300)\) 堆石子排成一排,其编号为 \(1,2,3,\cdots,N\)。每堆石子有一定的质量 \(m_i(m_i \le 1000)\)。
现在要将这 \(N\) 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。
试找出一种合理的方法,使总的代价最小,并输出最小代价。
题目分析
辨别区间 \(\rm DP\) 的方式:
从小区间延伸到大区间(合并等方式)。
许多括号数数题就是区间 \(\rm DP\) 好题,今年 \(\verb!2021-CSP-S-括号序列!\) 不妨一做。
令 \(dp[l][r]\) 表示区间 \([l,r]\) 内合并的最小代价。
枚举左右区间,再枚举中间断点来判断是否答案会变得更优:
\(dp[l][r]=\min\{dp[l][k]+dp[k+1][r]+cost\}(l\le k\lt r)\)。
其中 \(cost\) 表示 \(\sum\limits_{i=l}^{r}a_i\)。
枚举区间(左右+断点)复杂度为 \(O(N^3)\),中间枚举价值的部分又是 \(O(N)\),总时间复杂度 \(O(N^4)\)。
枚举价值部分可以通过前缀和优化,优化为 \(O(N^3)\)。
代码
//2022/1/6
const int INF=0x3f3f3f3f;
const int ma=305;
int a[ma],sum[ma];
int dp[ma][ma];
int n;
int main(void)
{
n=read();
Input_Int(n,a);
for(register int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
}
for(register int len=2;len<=n;len++)
{
for(register int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
dp[l][r]=INF;
for(register int k=l;k