[NOI1995]石子合并
原题链接:[NOI1995]石子合并
题目大意
给你(n)堆石子,现在要相邻两堆合成一堆,直到所有石子都在一堆里面为止,然后 1 号位的石子可以和 (n) 号位的石子合并,也就是说,这些石子堆围成了一个环。
每次合并有一个分数,这个分数就是新合并的石子堆的石子数,求最终的最小得分和最大得分
题目题解
第一次写区间DP,蛮好,区间DP相比于其他的DP至少有一个固定的模板和思维方向,也就是常说的挺套路的,看了下题解就明白了了
转移方程
(f_{i, j} = f_{i, k} + f_{k + 1, j} + d(i, j)) (d)为合并的分数
因为要满足DP的无后效性且一定要由已知推未知,于是区间dp也有一些改动,详细看代码
//#define fre yes
#include <cstdio>
#include <cstring>
#include <iostream>
const int N = 305;
int arr[N];
int f1[N][N], f2[N][N];
int s[N];
int main() {
static int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
arr[n + i] = arr[i];
}
for (int i = 1; i <= n * 2; i++) {
s[i] = s[i - 1] + arr[i];
}
for (int p = 1; p < n; p++) {
for (int i = 1, j = i + p; j <= 2 * n && i <= 2 * n; i++, j++) {
f2[i][j] = 1e9;
for (int k = i; k < j; k++) {
f1[i][j] = std::max(f1[i][j], f1[i][k] + f1[k + 1][j] + s[j] - s[i - 1]);
f2[i][j] = std::min(f2[i][j], f2[i][k] + f2[k + 1][j] + s[j] - s[i - 1]);
}
}
}
int minn = 1e9, maxx = 0;
for (int i = 1; i <= n; i++) {
maxx = std::max(maxx, f1[i][i + n - 1]);
minn = std::min(minn, f2[i][i + n - 1]);
} printf("%d
%d", minn, maxx);
}