[区间DP] 洛谷P1880 石子合并(NOI1995)(环转化序列)(前缀和)

题目

LP1880

思路

本题的数据量很小。。。就能让我很偷懒地使用一种投机取巧的方法。。
本题如果去掉是个环,那就是个普通的不能再普通的区间DP,参考区间DP例题矩阵链乘。我也刚开始没看到是个环,写完了样例都过不了。。
后来想了一段时间,想过用DAG模型,但是总感觉还是脱离不了区间DP,难不成把DAG魔改一下?。。
最后突然想到,其实可以把环暴力转换成序列,就是一个环的序列形式最多有它的长度那么多种。都暴力枚举出来,然后依次做区间DP,最后 O ( n 3 ) 足够应付本题的数据量。。
但我觉得我还是应该看看正解大佬们是什么样的。。
好吧都是这么写的。。。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;

const int INF = 1 << 28;
const int maxn = 100 + 10;
int n, A[maxn], B[maxn], S[maxn], maxd[maxn][maxn], mind[maxn][maxn];

int main() {
    scanf("%d", &n);
    _rep(i, 1, n) scanf("%d", &B[i]);

    int minans = INF, maxans = -INF;

    _for(v, 0, n) {
        // 生成数组A
        _rep(i, 1, n) {
            if (v + i <= n) A[i+v] = B[i];
            else A[(i + v) % n] = B[i];
        }

        // 计算前缀和S
        _rep(i, 1, n) S[i] = S[i - 1] + A[i];

        // 区间DP的特殊枚举顺序
        int j;
        _rep(l, 1, n)
            _rep(i, 1, n) {
            j = i + l;
            if (j > n) continue;
            maxd[i][j] = 0;
            mind[i][j] = INF;
            _rep(k, i, j - 1) {
                maxd[i][j] = max(maxd[i][j], maxd[i][k] + maxd[k + 1][j] + S[j] - S[i - 1]);
                mind[i][j] = min(mind[i][j], mind[i][k] + mind[k + 1][j] + S[j] - S[i - 1]);
            }
        }
        minans = min(minans, mind[1][n]);
        maxans = max(maxans, maxd[1][n]);
    }

    printf("%d\n%d\n", minans, maxans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/icecab/article/details/80959696