P1880 [NOI1995] 石子合并(环形区间dp模板)

题目描述:

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

试设计出一个算法,计算出将 NN 堆石子合并成 11 堆的最小得分和最大得分。

输入格式

数据的第 11 行是正整数 NN,表示有 NN 堆石子。

第 22 行有 NN 个整数,第 ii 个整数 a_iai 表示第 ii 堆石子的个数。

输出格式

输出共 22 行,第 11 行为最小得分,第 22 行为最大得分。

输入输出样例

输入 #1
4
4 5 9 4
输出 #1
43
54

说明/提示

1\leq N\leq 1001N100,0\leq a_i\leq 200ai20。

思路:我最开始接触dp时看到最可怕的题,明明是入门题对当时的我来说却十分困难,不过经过了接近一年的从无到有的过程,

我也算是收获了许多,对于一些模板题也有了自己的理解,今天再次看到这道题,总算是再也不用抓耳挠腮,无从下手,最后还去

题解区看记忆化搜索的方法,真的特别感谢我的学长和其他支持我的人。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[205];
int dp[205][205][2];
int sum[205];
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        a[i + n] = a[i];
    }
    if (n == 1) {
        printf("0\n");
        printf("0\n");
        return 0;
    }
    for (int i = 1; i <=2*n; i++) {//形成环则扩大为2n
        sum[i] = sum[i - 1] + a[i];
    }
    for (int i = 1, j = i + 1; j <2*n; i++, j++) {
        dp[i][j][0] = dp[i][j][1] = sum[j] - sum[i - 1];
    }
    for (int len = 3; len <= n; len++) {
        for (int i = 1, j = i + len - 1; j < 2*n; i++, j++) {
            dp[i][j][0] = 0x3f3f3f3f;
            for (int k = i; k < j; k++) {//从中间找分界点
                dp[i][j][1] = max(dp[i][j][1], dp[i][k][1] + dp[k + 1][j][1] + sum[j] - sum[i - 1]);
                dp[i][j][0] = min(dp[i][j][0], dp[i][k][0] + dp[k + 1][j][0] + sum[j] - sum[i - 1]);
            }
        }
    }
    int ans1 = 0x3f3f3f3f, ans2 = 0;
    for (int i = 1; i <=n; i++) {
        ans1 = min(ans1, dp[i][i + n-1][0]);
        ans2 = max(ans2, dp[i][i + n-1][1]);
    }
    cout << ans1 << endl;
    cout << ans2 << endl;
    return 0;
}

 

posted @ 2021-03-21 23:34  cono奇犽哒  阅读(41)  评论(0)    收藏  举报