跳转至

[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\)

实现

dp

代码

#include<bits/stdc++.h>
using namespace std;
const int N=300,MAXN=1e9;
int n,ans1,ans2,f1[N][N],f2[N][N],qzh[N],a[N];
/////////////ACACACACACACACACAC//////////////////
int d(int i,int j) {
    return qzh[j]-qzh[i-1];
}
/////////////ACACACACACACACACAC//////////////////
void pre() {
    for(int i=1; i<=2*n; i++) {
        qzh[i]=a[i]+qzh[i-1];
    }
}
/////////////ACACACACACACACACAC//////////////////
int main() {
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>a[i];
        a[n+i]=a[i];
    }
    //memset(f2,0x3f,sizeof f2);
    pre();
    ans2=MAXN;
    for(int p=1; p<n; p++)
        for(int i=1,j=i+p; i<2*n&&j<2*n; i++,j++) {
            f2[i][j]=MAXN;
            for(int k=i; k<j; k++) {
                f1[i][j] = max( f1[i][j],f1[i][k] + f1[k+1][j] + d(i,j));
                f2[i][j] = min(f2[i][j],f2[i][k] + f2[k+1][j] + d(i,j));
                ans1=max(ans1,f1[i][j]);
            }
        }
    for(int i=1; i<=n; i++) {
        ans2=min(ans2,f2[i][i+n-1]);
    }
    cout<<ans2<<endl<<ans1<<endl;
    return 0;
}