题目描述
在一个圆形操场的四周摆放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
这其实就是一个区间dp的入门题,首先应该用到拆环,首先解释一下什么叫做拆环。
1–2--3–4假如说是一个环形结构,那么4相邻的是1和3 1相邻的是2和4
所以直接复制一倍,接在后面
如上面的例子 1-2-3-4 1-2-3-4-1-2-3-4 发现满足。
那么接下来就可以写转移方程了,强烈建议读者自己调试,转移方程这个东西,看是看不懂的。只有自己调
可以发现每一个堆都是一个连续的区间,都可用左右端点来表示,那么设它的最大代价为
dp[i][j], 最小代价为dp1[i][j],转移方程在代码里面。
//f[i][j]表示i j完全合并最多能得到的 f[i][j]=max(f[i][k]+f[k+1][j] )+和 因为先把这两堆各自合并,然后再合并这两堆
#include<bits/stdc++.h>
using namespace std;
const int maxn=202;
int a[maxn],b[maxn*2+20],sum[maxn*2+20];
int dp[maxn*2+10][maxn*2+10],dp1[maxn*20][maxn*20];
int main() {
int n;
cin>>n;
int ans_max=0,ans_min=INT_MAX;
for(int i=1; i<=n; i++)cin>>a[i];
int now=0;
for(int i=1; i<=n; i++)b[++now]=a[i],sum[now]=a[i]+sum[now-1];
for(int i=1; i<=n; i++)b[++now]=a[i],sum[now]=a[i]+sum[now-1];//拆环的话直接复制一倍,接在后面
for(int k=1; k<=n; k++) {
for(int r=1; r+k-1<=n*2; r++) {
int ans=0,need=sum[k+r-1]-sum[r-1],ans1=INT_MAX;
for(int j=r; j<=k+r-2; j++) {
int ans0=0,ans00=0;
if(r!=j)ans0+=dp[r][j],ans00+=dp1[r][j];
if(r+k-1!=j+1)ans0+=dp[j+1][r+k-1],ans00+=dp1[j+1][r+k-1];
ans=max(ans,ans0);
ans1=min(ans1,ans00);
}
if(k==1||k==2)ans=0,ans1=0;
dp[r][k+r-1]=ans+need;
dp1[r][k+r-1]=ans1+need;
if(k==n) {
ans_max=max(ans_max,dp[r][r+k-1]);
ans_min=min(ans_min,dp1[r][r+k-1]);
}
}
}
// for(int i=1; i<=n; i++)
//for(int j=1; j<=n; j++)
//cout<<"dp1["<<i<<"]["<<i+j-1<<"]="<<dp1[i][i+j-1]<<endl;
cout<<ans_min<<endl<<ans_max;
}