Description
在一个圆形操场的四周摆放着n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
编程任务:
对于给定n堆石子,编程计算合并成一堆的最小得分和最大得分。
Input
输入包括多组测试数据,每组测试数据包括两行。
第1 行是正整数n,1<=n<=100,表示有n堆石子。
第2行有n个数,分别表示每堆石子的个数。
Output
对于每组输入数据,输出两行。
第1 行中的数是最小得分;第2 行中的数是最大得分。
思路
DP
f[i][j]=从i开始数j个的最#得分
因为是环形,所以超出的部分要取%,同时前缀和也要处理一下。
公式:
i到i+j-1,分个界k,取前部分+后部分的最小得分。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int n,s[102]={0},f[204][102],d[204][102];
int ss(int i,int j) {
if(j<=n) return s[j]-s[i-1];
return s[n]-s[i-1]+s[j-n];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
int a;
scanf("%d",&a);
s[i]=s[i-1]+a; //前缀
}
for(int j=2;j<=n;++j)
for(int i=1;i<=n;++i){
f[i][j]=10000000; //求最小值,先赋值一个足够大的数
for(int k=1;k<j;++k){
f[i][j]=min(f[i][k]+f[(i+k-1)%n+1][j-k]+ss(i,i+j-1),f[i][j]);
d[i][j]=max(d[i][k]+d[(i+k-1)%n+1][j-k]+ss(i,i+j-1),d[i][j]);
}
}
int ans=10000000;
int ansa=0;
for(int i=1;i<=n;++i){
if(f[i][n]<ans) ans=f[i][n];
if(d[i][n]>ansa) ansa=d[i][n];
}
printf("%d\n%d",ans,ansa);
}