P1880 [NOI1995] 石子合并 题解

2023-08-05,,

区间DP。

首先将其复制一遍(因为是环),也就是经典的破环成链。

设 \(f[i][j]\) 表示将 \(i\) 到 \(j\) 段的石子合并需要的次数。

\[f[i][j] = 0(i = j)
\]
\[f[i][j] = min(max)\{f[i][k] + f[k + 1][j] + \sum_{k = i }^{j}a[k](i \leq k < j)\}
\]

#include <iostream>
#include <cstring>
#include <algorithm> using namespace std; const int N = 210; int n;
int a[N];
int s[N];
int f[N][N];
int g[N][N]; signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], a[i + n] = a[i];
for (int i = 1; i <= n * 2; i++) s[i] = s[i - 1] + a[i]; memset(f, 0x3f, sizeof(f));
memset(g, 0xcf, sizeof(g)); for (int len = 1; len <= n; len++) {
for (int i = 1; i + len - 1 <= n * 2; i++) {
int j = i + len - 1;
if (len == 1) {
f[i][j] = 0;
g[i][j] = 0;
}
else {
for (int k = i; k < j; k++) {
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
g[i][j] = max(g[i][j], g[i][k] + g[k + 1][j] + s[j] - s[i - 1]);
}
}
}
}
int ans1 = 0x3f3f3f3f, ans2 = 0xcfcfcfcf; for (int i = 1; i <= n; i++) ans1 = min(ans1, f[i][i + n - 1]), ans2 = max(ans2, g[i][i + n - 1]);
cout << ans1 << '\n' << ans2 << '\n';
return 0;
}

P1880 [NOI1995] 石子合并 题解的相关教程结束。

《P1880 [NOI1995] 石子合并 题解.doc》

下载本文的Word格式文档,以方便收藏与打印。