题目描述
将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。
规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数 n 及每堆的石子数,并进行如下计算:
选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
选择一种合并石子的方案,使得做 n−1 次合并得分总和最小。
输入格式
第一行包含整数 n,表示共有 n 堆石子。
第二行包含 n 个整数,分别表示每堆石子的数量。
输出格式
输出共两行:
第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
数据范围
1≤n≤200
输入样例:
4
4 5 9 4
输出样例:
43
54
区间DP
从第n个石子累加到第一个,第二个,直到第n-1个,所以最多需要n*2个长度
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int f[330][330];
int sum[330];
int a[100101];
int g[330][330];
int main()
{
int n,i,j,k,len;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];
}
memset(g,-0x3f,sizeof(g));
memset(f,0x3f,sizeof(f));
for(i=1;i<=n*2;i++)
sum[i]=sum[i-1]+a[i];//前缀和
for(len=1;len<=n;len++)//枚举长度
for(i=1;i+len-1<=n*2;i++)//枚举起点
{
j=i+len-1;//确定终点
if(i==j)
g[i][j]=f[i][j]=0;
else
{
for(k=i;k<j;k++)
{
g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+sum[j]-sum[i-1]);
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
}
}
}
int maxx=-9999999;
int minn=9999999;
for(i=1;i<=n;i++)
{
maxx=max(maxx,g[i][i+n-1]);
minn=min(minn,f[i][i+n-1]);
}
printf("%d\n%d\n",minn,maxx);
}