洛谷P1880 [NOI1995]石子合并 #区间DP#

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入格式

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入 #1复制

4
4 5 9 4

输出 #1复制

43
54

题解

#include <bits/stdc++.h>
using namespace std;

const int maxn = 201;
const int inf = 1e9;
int n, sum, Min, Max, a[maxn], presum[maxn];
int dpmax[maxn][maxn], dpmin[maxn][maxn];

int getMin(int i, int j)
{
    if (i == j) return 0;
    if (dpmin[i][j]) return dpmin[i][j];
    int res = inf;
    for (int k = i; k < j; k++)
        res = min(res, getMin(i, k) + getMin(k + 1, j) + presum[j] - presum[i - 1]);
    return dpmin[i][j] = res;
}

int getMax(int i, int j)
{
    if (i == j) return 0;
    if (dpmax[i][j]) return dpmax[i][j];
    int res = 0;
    for (int k = i; k < j; k++)
        res = max(res, getMax(i, k) + getMax(k + 1, j) + presum[j] - presum[i - 1]);
    return dpmax[i][j] = res;
}

int main()
{
    Min = inf;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
        a[i + n] = a[i];
        presum[i] = sum += a[i];
    }
    for (int i = 1; i <= n; i++)
        presum[i + n] = presum[i] + presum[n];
    for (int i = 1; i <= n; i++)
    {
        Min = min(Min, getMin(i, i + n - 1));
        Max = max(Max, getMax(i, i + n - 1));
    }
    printf("%d\n%d\n", Min, Max);
    return 0;
}

 

上一篇:得了,一文把前缀和给扒的干干净净了。


下一篇:862. 和至少为 K 的最短子数组-前缀和/数组/滑动窗口-困难