NOI1995:石子合并

题目链接

NOI1995 石子合并

题目描述

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

复制代码
4
4 5 9 4

样例输出 #1

复制代码
43
54

提示

1 ≤ N ≤ 100 1\leq N\leq 100 1≤N≤100, 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0≤ai≤20。

算法思想

每次只能选相邻的 2 2 2 堆合并成新的一堆,以每次合并为阶段进行区间型动态规划,以最小得分为例:

  • 状态表示: f i j fij fij表示从第 i i i堆石子一直合并到第 j j j堆石子的最小得分
  • 状态计算:根据最后一次合并的位置可以分为下面几种情况:
    • 将第 i i i堆石子和后面已经完成的 i + 1... j i+1...j i+1...j这堆石子合并,得到的分数为 f i i + f i + 1 j + s i . . . j fii+fi+1j+si...j fii+fi+1j+si...j
    • 将前面已经完成合并的 i . . . i + 1 i...i+1 i...i+1这堆石子和后面已经完成的 i + 2... j i+2...j i+2...j这堆石子合并,得到的分数为 f i i + 1 + f i + 2 j + s i . . . j fii+1+fi+2j+si...j fii+1+fi+2j+si...j
    • ...
    • 将前面已经完成合并的 i . . . k i...k i...k这堆石子和后面已经完成的 k + 1... j k+1...j k+1...j这堆石子合并,得到的分数为 f i i + 1 + f i + 2 j + s i . . . j fii+1+fi+2j+si...j fii+1+fi+2j+si...j
    • ...
    • 将前面已经完成合并的 i . . . j − 1 i...j-1 i...j−1这堆石子和第 j j j堆石子合并,得到的分数为 f i j − 1 + f j j + s i . . . j fij-1+fjj+si...j fij−1+fjj+si...j

f i j fij fij为以上情况的最小值。其中 s i . . . j si...j si...j表示本次合并得到的分数,也就是第 i i i堆石子到第 j j j堆石子的分数和,可以使用前缀和计算得到。

  • 初始状态:为计算最小值 f i j fij fij应初始化为无穷大; f i i fii fii表示就合并自己,初始值为0。

除此之外,由于是在一个圆形操场的四周 摆放 N N N 堆石子,也就是说可以从任何一点出发进行合并。因此,需要采用拆环为链的方式进行处理,最后求以任意起点开始分数的最小值。

时间复杂度

状态数为 n × n n\times n n×n,状态计算时需要枚举最后一次合并位置,因此时间复杂度为 O ( n 3 ) O(n^3) O(n3)。

代码实现

cpp 复制代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110 * 2, INF = 0x3f3f3f3f;
int f[N][N], g[N][N];
int a[N], s[N];

int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++) 
    {
        cin >> a[i];
        a[n + i] = a[i]; //拆环为链
    }
    //计算前缀和
    for(int i = 1; i < 2 * n; i ++) s[i] = s[i - 1] + a[i];
    //枚举每次合并的长度,最小长度为2    
    for(int len = 2; len <= n; len ++)
        for(int i = 1; i + len - 1 < 2 * n; i ++) //枚举开始合并的位置
        {
            int j = i + len - 1; //合并结束的位置
            f[i][j] = INF; //初始状态
            //枚举最后一次合并的位置k
            for(int k = i; k < j; k ++)
            {
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); //最小得分
                g[i][j] = max(g[i][j], g[i][k] + g[k + 1][j] + s[j] - s[i - 1]); //最大得分
            }
        }
    
    int minx = 1e9, maxx = 0;
    for(int i = 1; i <= n; i ++) //枚举合并起点
    {
        minx = min(minx, f[i][i + n - 1]);
        maxx = max(maxx, g[i][i + n - 1]);
    }
    cout << minx << '\n' << maxx;
    
    return 0;
}
相关推荐
千里码aicood1 分钟前
面向电商冷启动与数据稀疏性的协同过滤算法优化研究
大数据·算法·协同过滤
xxxiugou1233 分钟前
双指针解题秘籍:从入门到精通
c语言·数据结构·c++·算法
代码中介商3 分钟前
C++ 预约系统实战(一):项目总览与 C/S 架构设计
c语言·开发语言·c++
能源革命3 分钟前
TimesFM(Time Series Foundation Model)介绍
算法·能源
猎嘤一号7 分钟前
学术训练(一)文献检索与阅读
人工智能·算法·机器学习·科研入门
Doubbbbbbble云21 分钟前
多路归并算法在外部排序中的实现与优化
算法
沉淀的.晴天41 分钟前
FreeRTOS信号量
数据结构·算法
青山木1 小时前
Hot 100 --- 打家劫舍
java·数据结构·算法·leetcode·动态规划
云边有个稻草人1 小时前
Tera Term 鸿蒙 PC 适配全记录:用 ArkUI 与 Native C++ 重建多协议终端工作流
c++·stm32·harmonyos
牛油果子哥q1 小时前
多模态大模型工程入门:图文Embedding、图文RAG、图片解析、C++多模态接口封装实战
开发语言·c++·embedding