从指数级枚举合并顺序的暴力出发,利用相邻合并对应连续区间的性质,设区间 dp[l][r] 枚举最后一次合并的断点,用前缀和加速得到 O(n³) 区间 DP。
OJ: luogu
题目 ID: P1775
难度:普及
标签:动态规划区间dp前缀和
创建: 2026-06-19 18:22
更新: 2026-10-24 00:35
形式化题目
给定一排
暴力解法
思路
最直接的做法完全按题意模拟:每一轮枚举当前序列中所有相邻的位置,尝试把这一对合并掉,然后递归处理剩下的序列,直到只剩一堆为止。递归到边界时统计一路上付出的代价和,在所有分支里取最小值。
设 dfs(stones) 表示把当前序列 stones 合并成一堆的最小总代价。每一层枚举
dfs(stones):
若 stones 只剩一堆,返回 0
对每个相邻位置 i:
合并 stones[i] 与 stones[i+1],代价为二者之和
递归 dfs(合并后的序列)
返回所有分支中「本次代价 + 后续代价」的最小值这个写法覆盖了每一种合法的合并顺序,因此结果一定正确;但指数级的搜索规模决定了它只能处理很小的
代码
#include <bits/stdc++.h>
using namespace std;
int dfs(vector<int> stones) {
if (stones.size() == 1) {
return 0;
}
int best = INT_MAX;
for (int i = 0; i + 1 < (int)stones.size(); ++i) {
vector<int> next = stones;
int merged = next[i] + next[i + 1];
next[i] = merged;
next.erase(next.begin() + i + 1);
// 暴力枚举这一步选择哪一对相邻石子合并
best = min(best, merged + dfs(next));
}
return best;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> stones(n);
for (int i = 0; i < n; ++i) {
cin >> stones[i];
}
cout << dfs(stones) << '\n';
return 0;
}复杂度
每一层分支数约为当前堆数,递归深度为
瓶颈
暴力在每一步都关心「当前每堆的质量分布」,也就是整个序列现在的形态。但真正决定答案的结构被藏在了形态背后:无论中间过程如何,每一步合并的都是原序列某段连续区间,最终某次合并一定是把某段连续区间的左边半段和右边半段各合成一堆,再合并这两堆。
也就是说,同一个区间
正解
思路
把观察写成状态:设 dp[l][r] 表示把原序列中区间
注意
下面用样例
| 区间 |
区间和 | 枚举的断点 |
|
|---|---|---|---|
读表时注意两点。第一,区间和
区间和用前缀和
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n + 1), sum(n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
const int INF = 0x3f3f3f3f;
vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
// dp[l][r] 表示把区间 [l, r] 合并成一堆的最小代价
for (int len = 2; len <= n; ++len) {
for (int l = 1; l + len - 1 <= n; ++l) {
int r = l + len - 1;
dp[l][r] = INF;
int seg_sum = sum[r] - sum[l - 1];
for (int k = l; k < r; ++k) {
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + seg_sum);
}
}
}
cout << dp[1][n] << '\n';
return 0;
}复杂度
三重循环分别枚举区间长度、左端点和断点,时间复杂度
总结
这题的核心不是「怎么模拟合并」,而是识别出「只能合并相邻两堆」意味着每堆石子在任意时刻都对应原序列的一段连续区间。把视角从「枚举每一步操作」换成「枚举最后一次合并的断点」后,指数级的搜索就被折叠成了
图示解析
一图流解析把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。
