洛谷 P1880 石子合并 区间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








思路

定义状态

maxdp(i, j)表示i到j的最大值

mindp(i, j)表示i到j的最小值

则状态转移方程为:

maxdp(i, j) = MAX{maxdp(i, k) + maxdp(k + 1, j) + sum(i, j) }

mindp(i, j) =  MIN{mindp(i, k) + mindp(k + 1, j) + sum(i, j)}

ac代码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>
#include <set>
#include <map>
#include <cctype>
#include <list>
#include <cmath>
#include <bitset>
#include <queue>
#include <stack>
#include <sstream>
#include <functional>
#include <cassert>
using namespace std;

#define tmax(a, b) if (b > a) a = b
#define tmin(a, b) if (b < a) a = b
char inc() { char _[10]; scanf("%s", _); return _[0]; }
int ini() { int _; scanf("%d", &_); return _; }
long long inll() { long long _; scanf("%I64d", &_); return _; }
double ind() { double _; scanf("%lf", &_); return _; }
string ins() { string _; cin >> _; return _; }
int inl(char _[]) { if (!fgets(_, (int)1e8, stdin)) return -1; int i = strlen(_); if (_[i - 1] == '\n') _[--i] = 0; return i; }

typedef pair<int, int> pii;
typedef pair<char, char> pcc;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const LL lnf = 0x3f3f3f3f3f3f3f3f;
const double pi = 3.14159265358979323846;
const double eps = 1e-8;
const int mod = 100007;
const int maxn = 200+ 10;

int n, a[maxn], prefix[maxn];
int maxdp[maxn][maxn], mindp[maxn][maxn];

int main() {
    int CAS = 0;
    //std::ios::sync_with_stdio(0);
    //std::cin.tie(0);
#ifdef NIGHT_13
    freopen("in.txt", "r", stdin);
   // freopen("out.txt", "w", stdout);
    int time_night_13 = clock();
#endif // NIGHT_13

    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        a[i] = a[i + n] = ini();
        prefix[i] = prefix[i - 1] + a[i];
    }
    for (int i = n; i < 2 * n; ++i) prefix[i] = prefix[i - 1] + a[i];
    memset(mindp, 0x3f, sizeof mindp);
    for (int k = 0; k < n; ++k) { //len - 1
        for (int i = 0; i + k < 2 * n; ++i) { //i: start, i + k: end
            if (k == 0) maxdp[i][i + k] = mindp[i][i + k] = 0;
            else if (k == 1) maxdp[i][i + k] = mindp[i][i + k] = a[i] + a[i + 1];
            else
                for (int j = i; j < i + k; ++j) { //mid
                    tmax(maxdp[i][i + k], maxdp[i][j] + maxdp[j + 1][i + k] + prefix[i + k] - prefix[i - 1]);
                    tmin(mindp[i][i + k], mindp[i][j] + mindp[j + 1][i + k] + prefix[i + k] - prefix[i - 1]);
                }
        }
    }
    int maxans = -inf, minans = inf;
    for (int i = 0; i + n - 1 < 2 * n; ++i) {
        tmax(maxans, maxdp[i][i + n - 1]);
        tmin(minans, mindp[i][i + n - 1]);
    }
    printf("%d\n%d\n", minans, maxans);

#ifdef NIGHT_13
    fprintf(stderr, "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    fprintf(stderr, "\t   Time: %d ms", (int)clock() - time_night_13);
    fprintf(stderr, "\n...........................................\n\n");
#endif // NIGHT_13
    return 0;
}
















  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值