题目: 石子合并
题目描述
在一个圆形操场的四周摆放 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 堆石子合并成 堆的最小得分和最大得分。
输入格式
数据的第 行是正整数 ,表示有 堆石子。
第 行有 个整数,第 个整数 表示第 堆石子的个数。
输出格式
输出共 行,第 行为最小得分,第 行为最大得分。
说明/提示
题解
状态表示: 表示合并 之间的石子所需要的最小花费
状态转移方程:
对于 的值, 可以通过前缀和计算得出
且由于这些石子围成了一个环, 故可以将输入的数组扩大一倍, 变为 堆, 其中第 堆与第 堆的值是一样的, 然后对这 个堆进行计算, 其效果就和对环进行计算是一样的了
#include <cstring>
#include <iostream>
using namespace std;
const int N = 201;
int f[N][N], g[N][N], a[N];
int main() {
int n;
cin >> n;
// 由于要用 f 来求最小值, 所以需要将 f 中的元素设置为极大值
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; ++ i) {
cin >> a[i];
a[i + n] = a[i];
}
// 开始的最晚位置至少得是 n, 对应扩展后数组的最大下标为 n * 2 - 1
const int len = (n << 1) - 1;
for (int i = 1; i <= len; ++ i) {
a[i] += a[i - 1];
f[i][i] = 0;
}
for (int i = 2; i <= n; ++ i) { // 合并 i 堆石子
for (int l = 1; l + i - 1 <= len; ++ l) { // 从第 i 堆开始合并
for (int k = l, r = l + i - 1; k < r; ++ k) { // 合并到第 r 堆
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + a[r] - a[l - 1]);
g[l][r] = max(g[l][r], g[l][k] + g[k + 1][r] + a[r] - a[l - 1]);
}
}
}
int minAns = 0x3f3f3f3f, maxAns = -1;
for (int i = 1; i <= n; ++ i) {
minAns = min(minAns, f[i][i + n - 1]);
maxAns = max(maxAns, g[i][i + n - 1]);
}
cout << minAns << endl << maxAns;
return 0;
}