P1880 [NOI1995] 石子合并 题解

发布时间 2023-07-28 10:12:54作者: SunnyYuan

区间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;
}