##D - 石子合并问题
Description
在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
对于给定n堆石子,计算合并成一堆的最小得分和最大得分。
Input
输入数据的第1行是正整数n,1≤n≤100,表示有n堆石子。第二行有n个数,分别表示每堆石子的个数。
Output
输出数据有两行,第1行中的数是最小得分,第2行中的数是最大得分。
Sample
Input
4
4 4 5 9
Output
43
54
记录一手:
我们怎么才能找到合并的最大值或最小值。
以题中所为例
4
5 4
9
在这个圆形操场有多种方法比如4,4,9,5或4,5,9,4或5,9,4,4……
动态规划的思想最后肯定是先合并了3(n-)堆,最后合并另一堆;
代码:
#include <iostream>
using namespace std;
int n;
int dp[111][111];//d[i][j]表示从第i堆到第j堆得到最小或最大的分数
int p[111][111];//p[i][j]表示从第i堆到第j堆共有多少【石子】
int a[111];//a[i][j]表示第i堆有多少石子
void Init(int *a)
{
for(int i=1;i<=n;i++)
{
p[i][i]=a[i];//从第i堆合并到第i堆共有a[i]个石子
for(int j=i+1;j<=n;j++)//分别求得第i堆到第i+1堆第i+2堆……石子数量//
{
p[i][j]=p[i][j-1]+a[j];
}
}
}
int getMax()
{
for(int r=2;r<=n;r++)//r控制合并长度,r=2,i<=n-(r-1),j=i+(r-1),最大达到n则dp[i][j]最大表示到d[i][n]//
{
for(int i=1;i<=n-r+1;i++)
{
int j=i+r-1;//j-i=r-1,r每次增加1,比如r=2,d[i][j]可取d[1][2],d[2][3],r增大1d[i][j]可取d[1][3]d[2][4]//
dp[i][j]=dp[i+1][j]+p[i+1][j]+p[i][i];
for(int k=i+1;k<j;k++)
{
dp[i][j]=max(dp[i][j],dp[i][k]+p[i][k]+dp[k+1][j]+p[k+1][j]);//以k为分界,先求k前面的再求k后面的分数,比如求dp[1][4]就分别求(1堆和2到4堆)和(1到2,2到4)和(1到3堆,第4堆)。再以(1到2,2到4)为例d[1][4]=d[1][2]+d[3][4]+p[1][2]+p[3][4],先是(1,2)(3,4)分别合并得分,第二部合起来的两堆再合并得分为这两堆的石子总数之和//
}
}
}
return dp[1][n];
}
int getMin()
{
for(int r=2;r<=n;r++)
{
for(int i=1;i<=n-r+1;i++)
{
int j=i+r-1;
dp[i][j]=dp[i+1][j]+p[i+1][j]+p[i][i];
for(int k=i+1;k<j;k++)
{
dp[i][j]=min(dp[i][j],dp[k+1][j]+dp[i][k]+p[k+1][j]+p[i][k]);
}
}
}
return dp[1][n];
}
int main()
{
cin>>n;
int cnt=n;
int minn=99999;
int maxx=0;
for(int i=1;i<=n;i++)cin>>a[i];
while(cnt--)
{
Init(a);
int tempmax=getMax();
if(tempmax>maxx)maxx=tempmax;
Init(a);
int tempmin=getMin();
if(tempmin<minn)minn=tempmin;
int t=a[1];
for(int i=1;i<n;i++)a[i]=a[i+1];
a[n]=t;
}
cout<<minn<<endl<<maxx<<endl;
return 0;
}