题目链接:https://vjudge.net/problem/51Nod-1021
题意
N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。
例如:1 2 3 4
,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
输入
第1行:N(2 <= N <= 100)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
输出
输出最小合并代价
样例输入
4
1
2
3
4
样例输出
19
题解:
这道题目如果其实我自己接触的时候还是很早的,我很早很早之前就在一场比赛当中接触到了这道题目,那是很久很久之前的一个春天,我大一的时候的一场校赛中,我唯一没有做出来TLE的题,因为当时还不知道记忆化搜索这个概念。
所以我当时的解法是用的dfs,没有使用记忆化进行剪枝。
这里的记忆化搜索其实可以对应动态规划的状态转移方程。
首先我们可以回到这道题目里面来,可以看到:
区间 [1..N]
经过 N-1
次合并会获得最终的一个数,那么在经过 N-2
次合并的时候是会还剩2个数的,而且这两个数肯定还是连续的 a[i]
和 a[i+1]
,所以,如果我们设 dp[i][j]
表示合并区间 [i,j]
的最小代价,那么:
dp[i][j] = min(dp[i][k] + dp[k+1][j]) + sum(i,j) , 其中k=i,i+1,...,j-1
这里的 sum(i,j)
表示 a[i]+a[i+1]+...+a[j]
。
那么 dp[i][j]
怎么求呢,我们可以通过定义一个函数 dfs(i,j)
来求得,但是可以通过记忆化搜索进行剪枝优化,其实就是写DP的时候记录的一些中间结果对应的重叠子问题。
代码如下(这个代码我没有验证过正确性,不过应该是没有问题的,主要理解他的思想):
#include <iostream>
using namespace std;
const int maxn = 1010;
int n, a[maxn], dp[maxn][maxn], sum[maxn];
int dfs(int l, int r) {
if (l == r) return 0;
if (l+1 == r) {
return dp[l][r] = a[l] + a[r];
}
if (dp[l][r])
return dp[l][r];
for (int i = l; i < r; i ++) {
int tmp = dfs(l,i) + dfs(i+1, r) + sum[r] - sum[l-1];
if (!dp[l][r]) dp[l][r] = tmp;
else dp[l][r] = min(dp[l][r], tmp);
}
return dp[l][r];
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
sum[i] = sum[i-1] + a[i];
}
cout << dfs(1, n) << endl;
return 0;
}
原文地址:https://www.cnblogs.com/zifeiy/p/10716703.html