当前位置:首页 > 题解目录 > 正文内容

【题解】石子合并(环形)

亿万年的星光2个月前 (05-30)题解目录172

【题目描述】

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

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

【输入描述】

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

第 2 行有 N 个整数,第 i个整数ai表示第 i 堆石子的个数。

1≤N≤400,0≤ai≤20。

【输出描述】

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

【样例输入】

4
4 5 9 4

【样例输出】

43
54


【题目分析】

1.输入处理:

使用静态数组 a 存储石子堆,并通过复制实现破环成链(a[i + N] = a[i])。

前缀和数组 prefix 用于快速计算区间和。


2.DP 初始化:

dp_min[i][i] = 0 和 dp_max[i][i] = 0(合并单堆石子得分为 0)。

其他位置初始化为 INT_MAX(最小得分)和 INT_MIN(最大得分)。


3.区间 DP 计算:

三重循环:

外层循环枚举区间长度 len(从 2 到 N)。

中层循环枚举起点 i。

内层循环枚举分割点 k,更新 dp_min 和 dp_max。

区间和通过前缀和优化计算:sum = prefix[j + 1] - prefix[i]。


4.结果查询:

遍历所有可能的长度为 N 的区间,找到最小和最大得分。



破环成链:将环形问题转化为线性问题,避免复杂边界处理。

前缀和数组:将区间和计算从 O(N) 优化到 O(1)。

静态数组:避免动态内存分配,提高运行效率。


【参考答案】

#include <iostream>
#include <climits>
#include <algorithm>

using namespace std;

const int MAX_N = 410; // 最大石子堆数(稍大于 400)

int main() {
    int N;
    cin >> N;
    int a[MAX_N * 2]; // 破环成链后的数组
    int prefix[MAX_N * 2 + 1] = {0}; // 前缀和数组

    // 输入石子堆
    for (int i = 0; i < N; i++) {
        cin >> a[i];
        a[i + N] = a[i]; // 复制一份接在后面(破环成链)
    }

    // 计算前缀和
    for (int i = 1; i <= 2 * N; i++) {
        prefix[i] = prefix[i - 1] + a[i - 1];
    }

    // DP 数组
    int dp_min[MAX_N * 2][MAX_N * 2];
    int dp_max[MAX_N * 2][MAX_N * 2];

    // 初始化 DP 数组
    for (int i = 0; i < 2 * N; i++) {
        for (int j = 0; j < 2 * N; j++) {
            if (i == j) {
                dp_min[i][j] = 0; // 单堆石子合并得分为 0
                dp_max[i][j] = 0;
            } else {
                dp_min[i][j] = INT_MAX; // 初始化为最大值
                dp_max[i][j] = INT_MIN; // 初始化为最小值
            }
        }
    }

    // 区间 DP
    for (int len = 2; len <= N; len++) { // 枚举区间长度
        for (int i = 0; i + len - 1 < 2 * N; i++) { // 枚举起点
            int j = i + len - 1; // 区间终点
            for (int k = i; k < j; k++) { // 枚举分割点
                // 当前区间的石子总数
                int sum = prefix[j + 1] - prefix[i];
                // 更新最小得分
                dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[k + 1][j] + sum);
                // 更新最大得分
                dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[k + 1][j] + sum);
            }
        }
    }

    // 在所有可能的长度为 N 的区间中找最小和最大得分
    int min_score = INT_MAX;
    int max_score = INT_MIN;
    for (int i = 0; i < N; i++) {
        min_score = min(min_score, dp_min[i][i + N - 1]);
        max_score = max(max_score, dp_max[i][i + N - 1]);
    }

    cout << min_score << endl;
    cout << max_score << endl;

    return 0;
}


扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

分享给朋友:

相关文章

【题解】特殊的质数肋骨

【题目描述】农民约翰母牛总是产生最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组...

字符全排列

【题目描述】给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。我们假设对于小写字母有‘a’ <‘b’ < … <‘y’<‘z’,而且给定的字符串中的字母已经按照...

【题解】阶乘问题

2.阶乘问题(fac.cpp)【题目描述】给定一个正整数n,求出一个最小的整数m并使得m!的末尾连续的0的个数小于n。m!=1*2*3*4*...*m【输入描述】第一行n。【输出描述】一个整数m。【样...

【题解】红与黑

【题解】红与黑

【题目描述】有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。【输入】包括多组数据。每...

【题解】走出迷宫的最少步数

【题目描述】一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜...

【题解】怪盗基德的滑翔翼

【题解】怪盗基德的滑翔翼

【题目描述】怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。有一天,怪盗...