UVA10304 Optimal Binary Search Tree
假定,$a$ 数组已经排好序了。
设 $f_{l, r}$ 表示 $l$ 到 $r$ 区间建一颗排序二叉树后的最小代价,
枚举 $[l, r]$ 的根 $k$,因为已经有序,所以
$k$ 的左子树 $[l, k - 1]$,就是把 $[l, k - 1]$ 建一颗排序二叉树,所有点的深度总体加 1,即代价总体加$sum_{i=l}^{k - 1} (1 \times a_i)$,那么左子树的总代价是
$$f_{l, k - 1} + \sum_{i=l}^{k - 1}a_i$$
$k$ 的右子树 $[k + 1, r]$ 同理,右子树的总代价为
$$f_{k + 1, r} + \sum_{i = k + 1} ^ ra_i$$
故
$$f_{l, r} = f_{l, k - 1} + f_{k + 1, r} + \sum_{i=l}^{k - 1}a_i + \sum_{i = k + 1} ^ ra_i$$
简化一下得,
$$f_{l, r} = f_{l,k - 1} + f_{k + 1, r} + \sum_{i = l}^ra_i - a_k$$
注意一下边界条件,这题 $O(n^3)$ 就解决啦!
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 260;
int f[N][N];
int a[N], sum[N];
int main() {
int n;
while (scanf ("%d", &n) != EOF) {
memset (f, 0x3f, sizeof (f));
for (int i = 1; i <= n; ++i) {
scanf ("%d", &a[i]); f[i][i] = 0; // 初始化
sum[i] = sum[i - 1] + a[i]; // 计算前缀和,方便处理求区间和
}
for (int d = 2; d <= n; ++d) {
for (int l = 1; l <= n - d + 1; ++l) {
int r = l + d - 1;
f[l][r] = min (f[l + 1][r] - a[l], f[l][r - 1] - a[r]);// 处理边界:没有左子树(根为 l)或没有右子树(根为 r)
for (int k = l + 1; k <= r - 1; ++k) {
f[l][r] = min (f[l][r], f[l][k - 1] + f[k + 1][r] - a[k]);
}
f[l][r] += sum[r] - sum[l - 1];
}
}
printf ("%d\n", f[1][n]);
}
return 0;
}