问题描述:
在一个圆形操场的四周摆放着 n 堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的 2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将 n 堆石子合并成一堆的最小得分和最大得分
!
f[i][j]表示的是从i到j合并的最优的得分
i==j 就是一堆,不用合并 f[i][j]=0
i<j 就是从里面找两堆(因为最后是两堆合并产生的这个序列),然后找最小的f[i][k]+f[k+1][j],这加上sum(i,j),(sum(i,j)就是本次合并的得分),加起来就是i到j合并的得分,然后找最小值。
也就是
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=205;
int q[maxn];
int f[maxn][maxn];//从i到j的最优值
int s[maxn];//从0到i的和(不包括i)
const int inf=1e9;
int getSub(int i,int j)
{
return s[j+1]-s[i];
}
void dp(int n)
{
for(int i =0;i<2*n;++i) f[i][i]=0;
for(int len=2;len<=n;++len)
{
for(int i=0;i+len-1<2*n;++i)
{
int j=i+len-1;
int ans=inf;
for(int k=i;k<j;++k)
ans=min(ans,f[i][k]+f[k+1][j]+getSub(i,j));
//当前的得分是sum,但是得加上之前的得分
//之前合并的和+ 这次合并的和
f[i][j]=ans;
}
}
}
void dp2(int n)
{
for(int i =0;i<2*n;++i) f[i][i]=0;
for(int len=2;len<=n;++len)
{
for(int i=0;i+len-1<2*n;++i)
{
int j=i+len-1;
int ans=-inf;
for(int k=i;k<j;++k)
ans=max(ans,f[i][k]+f[k+1][j]+getSub(i,j));
f[i][j]=ans;
}
}
}
int main()
{
int n;
cin>>n;
for(int i =0 ;i<n;++i) cin>>q[i];
//构造环形
for(int i=n;i<2*n;++i) q[i]=q[i-n];
//构造s[n]------------------
s[0]=0;
//这样构造感觉不太好。。。。
for(int i=1;i<=2*n;++i) s[i]=q[i-1]+s[i-1];
dp(n);
int ans=inf;
for(int i=0,j=i+n-1;j<2*n;++i,++j) ans=min(ans,f[i][j]);
cout<<ans<<endl;
dp2(n);
ans=-inf;
for(int i=0,j=i+n-1;j<2*n;++i,++j) ans=max(ans,f[i][j]);
cout<<ans<<endl;
}
~
dp好难,老师留的题都不太会写(两周了才写了一个)