题意:合并 N 堆石子,每次只能合并相邻的两堆石子,求最小代价
解题思路:
关键点:最后一次合并一定是左边连续的一部分和右边连续的一部分进行合并
状态表示:f[i][j] 表示将 i到 j 这一段石子合并成一堆的方案的集合,属性 Min
状态计算:
(1) i<j 时,f[i][j]=min
(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 len = 1; len <= n; len++) { // 区间长度
for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
int j = i + len - 1; // 区间终点
if (len == 1) {
dp[i][j] = 初始值
continue;
}
for (int k = i; k < j; k++) { // 枚举分割点,构造状态转移方程
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
}
}
}
/*
* @Author: wxy
* @Date: 2022-07-16 15:23:04
* @LastEditors: wxy
* @LastEditTime: 2022-07-16 15:37:59
* @FilePath: \AcWing\算法基础课\5.动态规划\3.区间DP\石子合并.cpp
* @Description:
* @
* @Copyright (c) 2022 by Author, All Rights Reserved.
*/
#include<iostream>
#include<algorithm>
#include<cstring>
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];
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;
for(int k = i; k < j; k ++)
f[i][j] = min(f[i][j],f[i][k] + f[k+1][j] + s[j] - s[i-1]);
}
cout << f[1][n] << endl;
return 0;
}