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;
}
posted @ 2022-04-20 11:57  wangzhongyuan  阅读(1)  评论(0编辑  收藏  举报  来源