一、问题描述
问题描述:在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选择相邻的两堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
算法设计:试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
样例输入:由input.txt提供输入数据
样例输出:输出数据保存到output.txt
输入文件示例:input.txt
4
4 4 5 9
输出文件示例:output.txt
43
54
二、问题分析
i表示从第i堆石头开始合并,j表示从i开始数j堆进行合并,0表示最低得分,1表示最高得分
所以m[i][j][0]表示合并的最低得分
同理,m[i][j][1]表示合并的最高得分
由于一开始还没有合并,则m[i][1][0] = m[i][1][1] = 0
sum(i,j)表示从i开始数j个数的和,例如sum(1,2) = a[1]+a[2]
首先两两合并,假设n=5
则m[1][2][0] = m[1][1][0]+m[2][1][0]+sum(1,2)
m[2][2][0] = m[2][1][0]+m[3][1][0]+sum(2,2)
…
然后三三合并计算m[1][3][0],m[2][3][0]…m[5][3][0]
…
最后五五合并计算m[1][5][0],m[2][5][0]…m[5][5][0]
所以得出递归方程
m[i][j][0] = min{ m[i][s][0]+m[r][j-s][0]+sum(i,j) } 1≤s<j
其中 r = (i+s-1)%n+1,因为是环形的,所以不能直接写m[i+s][j-s][0]
同理,m[i][j][1] = max{ m[i][s][0]+m[r][j-s]+sum(i,j) } 1≤s<j
边界条件为:m[i][1][0] = m[i][1][1] = 0
三、代码
#include<stdio.h>
#define MaxSize 100
int m[MaxSize+1][MaxSize+1][3];
void MinMax(int i,int j,int n,int s,int a[],int &minf,int &maxf){
int r = (i+s-1)%n+1;
int A = m[i][s][0],B = m[r][j-s][0],C = m[i][s][1],D = m[r][j-s][1];
int sum = 0;
if(i+j-1 > n){
for(int k = 1; k <= (i+j-1)%n; k++)
sum += a[k];
for(int k = i; k <= n; k++)
sum += a[k];
}else
for(int k = i; k <= i+j-1; k++)
sum += a[k];
minf = A + B + sum;
maxf = C + D + sum;
}
void solve(int n,int a[]){
//初始化
for(int i = 1; i <= n; i++){
m[i][1][0] = 0;
m[i][1][1] = 0;
for(int j = 2; j <= n; j++){
m[i][j][0] = 9999;
m[i][j][1] = 0;
}
}
for(int j = 2; j <= n; j++){
for(int i = 1; i <= n; i++){
for(int s = 1; s < j; s++){
int minf,maxf;
MinMax(i,j,n,s,a,minf,maxf);
if(minf < m[i][j][0])
m[i][j][0] = minf;
if(maxf > m[i][j][1])
m[i][j][1] = maxf;
}
}
}
int min = 9999,max = 0;
for(int i = 1; i <= n; i++){
if(min > m[i][n][0])
min = m[i][n][0];
if(max < m[i][n][1])
max = m[i][n][1];
}
printf("\n最小得分:%d\n",min);
printf("最大得分:%d",max);
}
int main(){
int n;
scanf("%d",&n);
int a[n+1];
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
solve(n,a);
return 0;
}