【题解】石子合并
【题目描述】
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
设计一个程序,计算出将N堆石子合并成一堆的最小得分。
【输入描述】
第一行为一个正整数N (2≤N≤100);
以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1≤i≤N)。
【输出描述】
为一个正整数,即最小得分。
【样例输入】
7 13 7 8 16 21 4 18
【样例输出】
239
【题目分析】
1.定义状态:
设 dp[i][j] 表示合并第 i 堆到第 j 堆石子的最小得分。
为了方便计算区间和,预处理一个前缀和数组 sum,其中 sum[i] 表示前 i 堆石子的总数。
2.状态转移方程:
对于区间 [i, j],枚举最后一次合并的分割点 k(i ≤ k < j),将区间分成 [i, k] 和 [k+1, j]。
合并这两部分的得分为 sum[j] - sum[i-1](即区间 [i, j] 的石子总数)。
状态转移方程为:
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
3.初始化:
单个石堆不需要合并,因此 dp[i][i] = 0。
其他 dp[i][j] 初始化为一个较大值(如 INT_MAX)。
4.计算顺序:
按区间长度从小到大计算,因为长区间的结果依赖于短区间。
【参考答案】
#include <iostream>
#include <climits>
using namespace std;
const int MAX_N = 100; // 最大石子堆数
int main() {
int N;
cin >> N;
int stones[MAX_N + 1] = {0}; // 石子堆,从1开始编号
int sum[MAX_N + 1] = {0}; // 前缀和数组
for (int i = 1; i <= N; i++) {
cin >> stones[i];
sum[i] = sum[i - 1] + stones[i];
}
// DP表,dp[i][j]表示合并i到j堆的最小得分
int dp[MAX_N + 1][MAX_N + 1] = {0};
// 初始化:单个石堆不需要合并,得分为0
for (int i = 1; i <= N; i++) {
dp[i][i] = 0;
}
// 按区间长度从小到大计算
for (int len = 2; len <= N; len++) { // 区间长度
for (int i = 1; i + len - 1 <= N; i++) { // 区间起点
int j = i + len - 1; // 区间终点
dp[i][j] = INT_MAX;
for (int k = i; k < j; k++) { // 枚举分割点
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]);
}
}
}
cout << dp[1][N] << endl;
return 0;
}扫描二维码推送至手机访问。
版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。