区间dp-基础题目1(石子合并)

题目链接

解题思路

我们把合并 1 至 n 1至n 1至n堆石子拆解成一个个子问题:

因为合并相邻两堆石子的值我们是可以确定的,已知任意两堆相邻的石子合并的代价之后,对于三堆石子,我们可以考虑两种方法

  • 先合并 1 至 2 1至2 1至2堆,再把前 2 2 2堆和第 3 3 3堆石子合并。
  • 先合并 2 至 3 2至3 2至3堆,再把后 2 2 2堆和第 1 1 1堆石子合并。

对于题目样例,4堆石子,代价分别为4,5,9,4。

①先考虑合并两堆石子的情况

合并区间 1,2 2,3 3,4
代价 9 14 13

②考虑合并三堆石子的情况:

1)前三堆石子合并,如果先合并 1 至 2 1至2 1至2堆,再把前 2 2 2堆和第 3 3 3堆石子合并,代价为 9 + 4 + 5 + 9 = 27 9+4+5+9=27 9+4+5+9=27;如果先合并 2 至 3 2至3 2至3堆,再把后 2 2 2堆和第 1 1 1堆石子合并,代价为 9 + 5 + 4 + 5 + 9 = 32 9+5+4+5+9=32 9+5+4+5+9=32。取较小值,27。

2)后三堆石子合并,如果先合并 2 至 3 2至3 2至3堆,再把 2 至 3 2至3 2至3堆和第 4 4 4堆石子合并,代价为 5 + 9 + 5 + 9 + 4 = 32 5+9+5+9+4=32 5+9+5+9+4=32;如果先合并 3 至 4 3至4 3至4堆,再把 3 至 4 3至4 3至4堆和第 2 2 2堆石子合并,代价为 9 + 4 + 5 + 9 + 4 = 31 9+4+5+9+4=31 9+4+5+9+4=31。取较小值,31。

③考虑合并四堆石子的情况:

1)前三堆石子和第四堆石子合并,根据②,前三堆石子合并的最小代价为27,再和第四堆石子合并,代价为 27 + 4 + 5 + 9 + 4 = 49 27+4+5+9+4=49 27+4+5+9+4=49。

2)前两堆石子和后两堆石子合并,根据①,前两堆石子合并的最小代价为9,后两堆石子合并的最小代价为13,前两堆石子和后两堆石子合并的代价为 9 + 13 + 4 + 5 + 9 + 4 = 44 9+13+4+5+9+4=44 9+13+4+5+9+4=44。

3)第一堆石子和后三堆石子合并,根据②,后三堆石子合并的最小代价为31,一堆石子和后三堆石子合并的最小代价为 31 + 4 + 5 + 9 + 4 = 53 31+4+5+9+4=53 31+4+5+9+4=53。

实现算法

前面的样例模拟过程可以总结以下过程:

  • 我们设 d p l r dplr dplr为合并区间 l , r l,r l,r内的石子的最小代价。 d p dp dp数组初始化为0。
  • 我们通过前面的分析知道,合并区间 l , r l,r l,r内的石子的最小代价, d p l r = m i n ( d l k + d p k + 1 r ) + 区间 l , r 内所有石子的重量 ( l < = k < r ) dplr=min(dlk+dpk+1r)+区间l,r内所有石子的重量(l<=k<r) dplr=min(dlk+dpk+1r)+区间l,r内所有石子的重量(l<=k<r)。区间内石子的重量,我们实际上可以做一个计算前缀和的预处理,前缀和结果存储在数组 s s s中,这样查询区间 l , r l,r l,r内所有石子的重量就更高效了。
  • 做完准备工作后,执行以下过程:
    1)我们从2开始枚举所有可能的长度 l n ln ln;
    2)在枚举长度的循环内,枚举区间的左右端点 l l l和 r r r, l l l从1开始枚举, r r r从 l + l n − 1 l+ln-1 l+ln−1开始枚举,结束条件是 r < = n r<=n r<=n;
    3)在枚举区间左右端点的循环内,我们还有枚举断点 k ( l < = k < r ) k(l<=k<r) k(l<=k<r),枚举 k k k的循环中执行状态转移方程: d p l r = m i n ( d p l r , d p l k + d p k + 1 r + s r − s l − 1 ) dplr=min(dplr,dplk+dpk+1r+sr-sl-1) dplr=min(dplr,dplk+dpk+1r+sr−sl−1)。
    4)最后输出答案: d p 1 n dp1n dp1n

AC代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m[1005], s[1005], dp[1005][1005];
signed main() {
	cin >> n;
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 1; i <= n; i++) {
		cin >> m[i];
		s[i] = s[i - 1] + m[i];
		dp[i][i] = 0;
	}
	for (int ln = 2; ln <= n; ln++) {
		for (int l = 1, r = l + ln - 1; r <= n; l++, r++) {
			for (int k = l; k < r; k++) {
				dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);
			}
		}
	}
	cout << dp[1][n];
	return 0;
}
相关推荐
JieE2128 小时前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2121 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack202 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树2 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2122 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2122 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术3 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦3 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050733 天前
(一)小红的数组操作
算法·编程语言