题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输入样例#1:
复制
4 4 5 9 4
输出样例#1:
复制
43 54
题解
对于这个题目,首先想到的是贪心,将和最小的合并到一起,但这种方法肯定是错误的,至于为什么(不会告诉你们我不知道)。
正题来了-对于这个题目,因为石子是一个环,所以一个常规的操作就是拆环成链,至于操作在代码里面有解释。
很容易想到动态转移方程(先是最小的)
f(i,j)=min{f(i,k−1),f(k,j)}+w(i,j)(i≤k≤j)。
f[i][j]代表从i合到j的最小代价,w[i][j]代表从i到j的重量总和。
我们类比到最大的
g(i,j)=max{g(i,k−1),g(k,j)}+w(i,j)(i≤k≤j)。
贴上我比较丑陋的代码
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 inline int read(){ 6 int w=1,s=0; 7 char ch=getchar(); 8 while(ch<'0'||ch>'9') {if (ch=='-') w=-1; ch=getchar();} 9 while(ch>='0'&&ch<='9') {s=s*10+ch-'0'; ch=getchar();} 10 return s*w; 11 } 12 13 int a[10000],f[1100][1100],dp[1100][1100],sum[10000],maxx=0,minn=9446413135; 14 15 int main(){ 16 int n=read(); 17 for(int i=1;i<=n;i++) a[i]=read(),a[i+n]=a[i];//这里便是拆环成链。 18 for(int i=1;i<=2*n;i++) sum[i]=sum[i-1]+a[i];//记录前缀 19 for(int i=2*n-1;i>=1;i--) 20 for(int j=i+1;j<n+i;j++){ 21 f[i][j]=0x3f3f3f3f; 22 for(int k=i;k<j;k++) 23 { 24 f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]); //动态转移方程 25 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); 26 } 27 } 28 for(int i=1;i<=n;i++){ 29 minn=min(minn,f[i][i+n-1]); //因为是拆环成链,从i带i+n-1这个长度为n的序列便是原来的环从i这个点拆出来的 30 maxx=max(maxx,dp[i][i+n-1]); 31 } 32 cout<<minn<<endl<<maxx; 33 return 0; 34 }