题面
【题目描述】
有一排石子,共n 堆。现要将石子有次序地合并成一堆。
规定每次只能选相邻的2 堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。
试设计一个算法,计算出将n堆石子合并成一堆的最小得分。
【输入】
第一行为一个正整数N(2<=N<=100);
以下N行,每行一个正整数,小于10000,分别表示第I堆石子的个数(1<=I<=N)。
【输出】
一个正整数,即最小得分。
【样例输入】
7
13
7
8
16
21
4
18
【样例输出】
239
算法分析
区间型DP
状态:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]——合并第
i
i
i堆石子~第
j
j
j堆石子的最小得分。
当第
i
i
i堆石子~ 第
j
j
j堆石子合并成一堆之前,肯定是由某两堆合并而来的,这里进行枚举,假设是由一堆(第
i
i
i堆石子 ~ 第
k
k
k堆石子合并后的一堆),另一堆(第
k
+
1
k+1
k+1堆石子 ~ 第
j
j
j堆石子合并后的一堆),这两堆合并。合并的得分为第
i
i
i堆石子~ 第
j
j
j堆石子的石子总数,这里可以使用前缀和。
状态转移方程
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
k
]
,
d
p
[
k
+
1
]
[
j
]
)
+
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
dp[i][j]=min( dp[i][k] , dp[k+1][j] ) + sum[i]-sum[j-1]
dp[i][j]=min(dp[i][k],dp[k+1][j])+sum[i]−sum[j−1],(
i
<
=
k
<
=
j
i<=k<=j
i<=k<=j,
s
u
m
[
i
]
sum[i]
sum[i]为前缀和)
边界
f
[
i
]
[
i
]
=
0
f[i][i]=0
f[i][i]=0,只有一堆时,得分为
0
0
0.
合并第
1
1
1堆~第
n
n
n堆的最小值就是
f
[
1
]
[
n
]
f[1][n]
f[1][n]。
参考程序
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int s[110],dp[110][110];
int main()
{
int n,a;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
s[i]=s[i-1]+a; //前缀和
}
memset(dp,127/3,sizeof(dp)); //初始化为较大值
for(int i=1;i<=n;i++) dp[i][i]=0; //边界条件
for(int i=n-1;i>=1;i--)
for(int j=i+1;j<=n;j++)
for(int k=i;k<=j-1;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+s[j]-s[i-1]);
cout<<dp[1][n]<<endl;
return 0;
}