-题目大意-
有排成一排的n堆石子,要将它们移动成一堆,每次只能移动相邻的两堆石子,且新的石子堆的石子数量为得到的分值,求将它们全部合并后能得到的最小分值。
-Input
第一行n
接下来一行a1,a2…an
-Ouput
最小分值
-方法-
DP,s为前缀和
PS:代码f写不习惯,习惯用a数组。
方法1
枚举边界,然后枚举长度
状态转移方程:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
i为边界起始点,从n-1到1;
j为边界结束点,从i+1到n;
k为分界(位置),从i后到j-1后分,俩块区域的最优解合并。
#include<cstdio>
int min(int a,int b){
if(a<b) return a;
return b;
}
int main(){
int n,a[101][101]={0},s[101]={0};
scanf("%d",&n);
for(int i=1;i<=n;++i){
int aa;
scanf("%d",&aa);
s[i]=s[i-1]+aa;
}
for(int i=n-1;i>=1;--i)
for(int j=i+1;j<=n;++j){
a[i][j]=a[i][i]+a[i+1][j]+s[j]-s[i-1];
for(int k=i+1;k<j;++k)
a[i][j]=min(a[i][j],a[i][k]+a[k+1][j]+s[j]-s[i-1]);
}
printf("%d",a[1][n]);
}
方法2
先枚举长度,再枚举边界
状态转移方程:
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1])
len为长度,从2到n;
i为边界起始点,从1到n-j+1(防止超界);
算出j,j为结束点,从i后到j-1后分,俩块区域的最优解合并。
k是分界(位置),从i后到j-1后分,俩块区域的最优解合并。
#include<cstdio>
int min(int a,int b){
if(a<b) return a;
return b;
}
int main(){
int n,a[101][101]={0},s[101]={0};
scanf("%d",&n);
for(int i=1;i<=n;++i){
int aa;
scanf("%d",&aa);
s[i]=s[i-1]+aa;
}
for(int l=2;l<=n;++l)
for(int i=1;i<=n-l+1;++i)
{
int j=i+l-1;
a[i][j]=10000000;
for(int k=i;k<=j-1;++k)
a[i][j]=min(a[i][j],a[i][k]+a[k+1][j]+s[j]-s[i-1]);
}
printf("%d",a[1][n]);
}
方法3
枚举长度,然后枚举边界
状态转移方程:
f[i][j]=min(f[i][k]+f[i+k][j-k]+s[i+j-1]-s[i-1],f[i][j]);
j为长度,从2到n;
i为边界起始点,从1到n-j+1;
k为分界(长度),从i后1个到j-1个后分,俩块区域的最优解合并。
#include<cstdio>
int min(int a,int b){
if(a<b) return a;
return b;
}
int main(){
int n,s[101]={0},a[101][101];
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-j+1;++i){
a[i][j]=10000000;
for(int k=1;k<j;++k)
a[i][j]=min(a[i][k]+a[i+k][j-k]+s[i+j-1]-s[i-1],a[i][j]);
}
printf("%d",a[1][n]);
}