时间:2022-07-22 10:55:13 | 栏目:C代码 | 点击:次
题意:
合并 N 堆石子,每次只能合并相邻的两堆石子,求最小代价
解题思路:
关键点:最后一次合并一定是左边连续的一部分和右边连续的一部分进行合并
状态表示:f[i][j]表示将 i 到 j 这一段石子合并成一堆的方案的集合,属性 Min
状态计算: (1) i<j 时,f[i][j]=min f[i][k]+f[k+1][j]+s[j]?s[i?1] (2)i=j 时,
f[i][i]=0(合并一堆石子代价为 0)
问题答案: f[1][n]
所有的区间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]); } } }
最后总的代码:
#include <iostream> #include <cstring> using namespace std; const int N = 307; int a[N], s[N]; int f[N][N]; int main() { int n; cin >> n; for (int i = 1; i <= n; i ++) { cin >> a[i]; s[i] += s[i - 1] + a[i]; } memset(f, 0x3f, sizeof f); // 区间 DP 枚举套路:长度+左端点 for (int len = 1; len <= n; len ++) { // len表示[i, j]的元素个数 for (int i = 1; i + len - 1 <= n; i ++) { int j = i + len - 1; // 自动得到右端点 if (len == 1) { f[i][j] = 0; // 边界初始化 continue; } for (int k = i; k <= j - 1; k ++) { // 必须满足k + 1 <= 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; return 0; }