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堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 100) 第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
Output
输出最小合并代价
Input示例
4 1 2 3 4
Output示例
19
题解:第一次知道四边形优化。。具体证明点击打开链接
知道了四边形优化后就是水题了。不过因为是环,所以要特殊处理一下。环的情况即以数组任意一个元素为开头枚举即可。但这样会T(n^3)故我们将长度为n的数组变成长度为2 * n的数组,每次处理时最多处理n个连续元素,复杂度较直线石子合并只多了常数乘项(4)
AC代码
#include <stdio.h>
#include <iostream>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cmath>
typedef long long ll;
using namespace std;
const ll maxn = 2e3 + 5, inf = 1e18 + 10;
ll sum[maxn], dp[maxn][maxn], s[maxn][maxn];
void init(){
memset(dp, 0, sizeof(dp));
memset(s, 0, sizeof(s));
memset(sum, 0, sizeof(sum));
for(int i = 1; i < maxn; i++){
s[i][i] = i;
for(int j = 1; j < maxn; j++)
dp[i][j] = inf;
dp[i][i] = 0;
}
}
int main(){
init();
ll n, t;
scanf("%lld", &n);
for(ll i = 1; i <= n; i++){
scanf("%lld", &t);
sum[i] = sum[i - 1] + t;
}
for(ll i = 1; i <= n; i++)
sum[i + n] = sum[n] + sum[i];
for(ll i = 2 * n; i >= 1; i--){
t = min(2 * n, i + n - 1);
for(ll j = i + 1; j <= t; j++){
for(ll k = s[i][j - 1]; k <= s[i + 1][j]; k++){
if(dp[i][j] > dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]){
s[i][j] = k;
dp[i][j] = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1];
}
}
}
}
ll ans = dp[1][n];
for(ll i = 2; i <= n; i++)
ans = min(ans, dp[i][i + n - 1]);
printf("%lld\n", ans);
return 0;
}