「区间DP」[NOI1995]石子合并

[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);
}

原文地址:https://www.cnblogs.com/Nicoppa/p/11505932.html