P1880 [NOI1995] 石子合并(区间dp)

P1880 [NOI1995] 石子合并

题目描述

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

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

输入格式

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

\(2\) 行有 \(N\) 个整数,第 \(i\) 个整数 \(a_i\) 表示第 \(i\) 堆石子的个数。

输出格式

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

输入输出样例 #1

输入 #1

4
4 5 9 4

输出 #1

43
54

说明/提示

\(1\leq N\leq 100\)\(0\leq a_i\leq 20\)
由于这道题石子是环形,我们可以开两倍的数组,然后循环列举起点,然后分别计算最大值和最小值

#include<iostream>
#include<cstring>
using namespace std;
const int N=500+5;
int s[N];
int Max[N][N];
int Min[N][N];
int MAX=-1;
int MIN=1e9;
int main(){
    int n;
    cin>>n;
    memset(Max,-0x3f,sizeof(Max));
    memset(Min,0x3f,sizeof(Min));
    for(int i=1;i<=n;i++){
        cin>>s[i];
        s[i+n]=s[i];
        Max[i][i]=0;
        Max[i+n][i+n]=0;
        Min[i][i]=0;
        Min[i+n][i+n]=0;
    }
    for(int i=1;i<=2*n;i++)s[i]+=s[i-1];
    for(int i=1;i<=n;i++){
        for(int len=2;len<=n;len++){
            for(int l=i;l+len-1<=n+i-1;l++){
                int r=l+len-1;
                for(int k=l;k<r;k++){
                    Max[l][r]=max(Max[l][r],Max[l][k]+Max[k+1][r]+s[r]-s[l-1]);
                    Min[l][r]=min(Min[l][r],Min[l][k]+Min[k+1][r]+s[r]-s[l-1]);
                }
               
            }
        }
        MAX=max(MAX,Max[i][n+i-1]);
        MIN=min(MIN,Min[i][n+i-1]);
    }
    cout<<MIN<<endl<<MAX;
    return 0;
}
posted @ 2025-02-25 16:26  郭轩均  阅读(65)  评论(0)    收藏  举报