在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
编程任务:
对于给定n堆石子,编程计算合并成一堆的最小得分和最大得分。
Input
输入包括多组测试数据,每组测试数据包括两行。
第1 行是正整数n,1<=n<=100,表示有n堆石子。
第2行有n个数,分别表示每堆石子的个数。
Output
对于每组输入数据,输出两行。
第1 行中的数是最小得分;第2 行中的数是最大得分。
Sample Input
4
4 4 5 9
Sample Output
43
54
解题思路
看这题之前请补一下:http://blog.csdn.net/mr_wuyongcong/article/details/78815773
这道题是石子合并的升级版,这里把改成了圆形,那么说明第一堆和最后一堆也可以合并,这道题我用了一种不同的方法。可以先把所有两个的和在一起,再把所有的三个的合在一起......以此类推。然后列出动态转移方程:
mins=min(mins,f[i][k]+f[k+1][j]+s[j]-s[i-1])
maxs=max(maxs,f1[i][k]+f1[k+1][j]+s[j]-s[i-1])
还是代码讲的清楚
代码
#include<cstdio>
#include<iostream>
using namespace std;
int n,x,s[201],f[201][201],f1[201][201],maxs,mins,a[201];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[n+i]=a[i];//环状相连
}
for (int i=1;i<=2*n;i++)
s[i]=s[i-1]+a[i];//预处理不解释
for (int ii=2;ii<=n;ii++)//如我所说↑
for (int i=1;i<=2*n-ii+1;i++)//枚举开头
{
int mins=2147483647,maxs=0,j=i+ii-1;//如我所说↑
for (int k=i;k<j;k++)//枚举分裂点
{
mins=min(mins,f[i][k]+f[k+1][j]+s[j]-s[i-1]);
maxs=max(maxs,f1[i][k]+f1[k+1][j]+s[j]-s[i-1]);
//动态转移方程
}
f[i][j]=mins;//最小值存入
f1[i][j]=maxs;//最大值存入
}
int mins=2147483647,maxs=0;
for (int i=1;i<=n;i++)
{
maxs=max(maxs,f1[i][i+n-1]); //求每个区域的最大值
mins=min(mins,f[i][i+n-1]); //求每个区域的最小值
}
printf("%d\n%d",mins,maxs);//get√
}