描述
有N堆石子排成一排,每堆石子有一定的数量。现要将N堆石子并成为一堆。合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆。求出总的代价最小值。
- 输入
- 有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 - 输出
- 输出总代价的最小值,占单独的一行
- 样例输入
-
3 1 2 3 7 13 7 8 16 21 4 18
- 样例输出
-
9 239
//区间DP 最小代价=合并和+总和 利用合并和的不同进行选择
//利用区间DP的方法 对不同的合并情况进行讨论
/*
区间型DP一般(也有例外)都是从小的区间开始求最优解,
然后不断扩大所求的区间,而求大区间时所用到的小区间前面已经求过了。so直接用就行啦。
区间内枚举最后一次的位置, 所以说区间动规一般都是三层for循环, 前两层用来控制区间长度,
最后一层用来枚举最后一次的位置, 还有需要注意的是区间用从小到大,
因为动态规划就是后面的用到前面的出的结果递推后面的结果。
dp[i][j] 表示从第 i 堆合并到第 j 堆的最小代价,
sum[i][j] 表示第 i 堆到第 j 堆的石子总和,
则动态转移方程:dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j]) (i <= k <= j - 1)。
举个例子吧:4个数(1,2,3, 4)
某区间(i到j)相距为1时 d = 1 可求出f[1][2] = 3; f[2][3] = 5; f[3][4] = 7;
d = 2时 , f[1][3] = min(f[1][2] + f[3][3], f[1][1] + f[2][3])+sum[1][3]= 9; (这里f[3][3] = 0,应为合并自己没花费)。同理f[2][4] = 14;
d = 3时:f[1][4] = 19;
枚举前一状态 f[1][4] = min(f[1][1]+f[2][4], f[1][2]+f[3][4], f[1][3] + f[4][4]) + sum[1][4];
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=220;
const int M=10e9;
int n,s[N][N],a[N],d[N][N];
void sum()
{
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
for(int k=i;k<=j;k++)
s[i][j]+=a[k];
}
}
}
int DFS(int x,int y)
{
if(d[x][y]!=10e8)return d[x][y];
for(int i=x;i<y;i++)
d[x][y]=min(d[x][y],DFS(x,i)+DFS(i+1,y)+s[x][y]);
return d[x][y];
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(s,0,sizeof(s));
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
d[i][j]=10e8;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
sum();
for(i=1;i<=n;i++)
d[i][i]=0;
int ans=DFS(1,n);
printf("%d
",ans);
}
return 0;
}