版权声明:欢迎借鉴,谢绝抄搬。 https://blog.csdn.net/ssllyf/article/details/84860275
Description
在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请设计一个程序,计算出将N堆石子合并成一堆的最小得分。
Input
每组数据第1行为一个正整数N(2<=N<=100),以下N行,每行一个正整数,小于10000,分别表示第i堆石子的个数(1<=i<=N)。
Output
对于每组数据输出一个正整数,即最小得分
Sample Input
7
13
7
8
16
21
4
18
Sample Output
239
说明:
本题之前用两个方法做过,这次用三个方法做
先枚举前面的数(i),再枚举后面的数(j),最后枚举中间的分割线(c)(三重循环) 用i到c-1之间的数加上c到j的数再加上之前的得分,再将所有的值去最小。(计算一个数到另一个数可以用前缀和搞定)
状态转移方程:
f[i][j]表示合成i到j的最小得分
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,s[101],f[101][101],x;
int main()
{
memset(f,127/3,sizeof(f));//因为要去最小的,所以要先赋一个大的值
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][i]=0;//清零单个的
}
for (int i=n-1;i>=1;i--)
for (int j=i+1;j<=n;j++)
for (int c=i+1;c<=j;c++)
f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
printf("%d",f[1][n]);
}
先枚举长度(k),再枚举前面的数(i),最后枚举分割线(c),然后后面的数(j)就可以求出来了:
之后的步骤和方法一 几乎 完全一样
状态转移方程:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,x,j,s[101],f[101][101];
int main()
{
memset(f,127/3,sizeof(f));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][i]=0;
}
for (int k=2;k<=n;k++)//复制长度
for (int i=1;i<=n-k+1;i++)
{
j=i+k-1;//求出后面的数
for (int c=i+1;c<=j;c++)
f[i][j]=min(f[i][j],f[i][c-1]+f[c][j]+s[j]-s[i-1]);
}
printf("%d",f[1][n]);
}
方法与前面的一样,但是f[i][j]表示的是从第i个开始后面的j个数,导致了写法不同
动态转移方程
i+c是第二段的开头,j-c是的二段的长度
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,x,s[101],f[101][101];
int main()
{
memset(f,127/3,sizeof(f));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
s[i]=s[i-1]+x;//前缀和
f[i][1]=0;
}
for (int j=2;j<=n;j++)//枚举长度,但表达不一样
for (int i=1;i<=n-j+1;i++)//枚举前面的数
for (int c=1;c<j;c++)//枚举分割线
f[i][j]=min(f[i][j],f[i][c]+f[i+c][j-c]+s[i+j-1]-s[i-1]);
printf("%d",f[1][n]);
}