Description
在一个
圆形操场
的四周摆放
N
堆石子
(N≤100)
,现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,读入堆数
N
及每堆石子数
(≤100)
选择一种合并石子的方案,分别得到合并这
N
堆石子为一堆,可以得到的最大得分和最小得分
Input
输入包含多个例子。第一行为
N
,即石子堆的数目,以下一行为
N
个整形,分别代表每堆石子的数目。当
N=0
时,输入结束。
Output
对每个例子,输出其最小得分和最大得分,这两个数值以空格间隔开,每个例子占一行。
Sample Input
6
30 35 15 5 10 20
3
1 2 3333
6
3 4 5 6 7 8
0
Sample Output
275 475
3339 6671
84 125
Hint
无
分析 : 动态规划的典型题目。 从合并相邻两个堆开始,三个堆。。。。一直到N个堆。
由于是环形,我延长了数组长度,用来将环形变换成线性。
以下代码
/*
*/
#include <bits/stdc++.h>
using namespace std ;
#define N 100
int dp[N*2][N*2] ; // dp(i,j) 表示第i堆石子到第j堆石子的合并后的最优值
int getCount(int a[],int i, int j){
int sum = 0 ;
for (;i<=j;++i)
sum+=a[i] ;
return sum ;
}
int getMaxScore(int a[] , int Num){
memset(dp,0,sizeof dp);
int maxscore = 0 ;
for (int l = 1; l < Num ;++l){//l 是合并后的长度
for (int i = 0 ; i < Num*2-1 ; ++i){// i 是合并的起始位置
if (i+l>=2*Num-1)
continue;
int count = getCount(a,i,i+l);
for (int k = 0 ; k < l ; k++ ){ //k代表合并时 第一堆的数量
dp[i][i+l] = max(dp[i][i+l] , dp[i][i+k]+dp[i+k+1][i+l]+count);
// printf("i=%d , k = %d , j = %d, dp=%d\n",i,k,i+l,dp[i][i+l]);
}
}
}
for (int i =0 ; i < Num ;++i){
maxscore = max(maxscore , dp[i][i+Num-1]) ;
}
return maxscore;
}
int getMinScore(int a[] , int Num){
memset(dp,0,sizeof dp);
int maxscore = 0x7fffffff ;
// cout<<"max"<<maxscore<<endl;
for (int l = 1; l < Num ;++l){//l+1 是合并后的长度
for (int i = 0 ; i < Num*2-1 ; ++i){// i 是合并的起始位置
if (i+l>=2*Num-1)
continue;
int count = getCount(a,i,i+l);
// cout<<"count :"<<count <<endl;
dp[i][i+l] = 0x7fffffff;
for (int k = 0 ; k < l ; k++ ){ //k代表合并时 第一堆的数量
dp[i][i+l] = min(dp[i][i+l] , dp[i][i+k]+dp[i+k+1][i+l]+count);
// printf("i=%d , k = %d , j = %d, dp=%d\n",i,k,i+l,dp[i][i+l]);
}
}
}
for (int i =0 ; i < Num ;++i){
maxscore = min(maxscore , dp[i][i+Num-1]) ;
}
return maxscore;
}
int main(){
int Num ;
int s[N];
while (cin>>Num&& Num!=0){
for (int i = 0 ; i < Num ; ++i){
cin>> s[i];
s[i+Num] = s[i] ;
}
cout<<"minscore "<<getMinScore(s,Num)<<endl;
cout<<"maxscore "<<getMaxScore(s,Num)<<endl;
}
return 0;
}