[NOIP2003 提高组] 加分二叉树
题目描述
设一个 个节点的二叉树 的中序遍历为,其中数字 为节点编号。每个节点都有一个分数(均为正整数),记第 个节点的分数为 , 及它的每个子树都有一个加分,任一棵子树 (也包含 本身)的加分计算方法如下:
的左子树的加分 的右子树的加分 的根的分数。
若某个子树为空,规定其加分为 ,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为 且加分最高的二叉树 。要求输出
-
的最高加分。
-
的前序遍历。
输入格式
第 行 个整数 ,为节点个数。
第 行 个用空格隔开的整数,为每个节点的分数
输出格式
第 行 个整数,为最高加分()。
第 行 个用空格隔开的整数,为该树的前序遍历。
样例 #1
样例输入 #1
5
5 7 1 2 10
样例输出 #1
145
3 1 2 4 5
提示
数据规模与约定
对于全部的测试点,保证 ,节点的分数是小于 的正整数,答案不超过 。
题目分析:
他的中序遍历 为 1 ,2 , 3 ,4,... ,n
样例 可以看出 最大高分 3 1 2 4 5 可得出图
由题意知 L 到 R 的加分公式为 l * r + k;
则 我们 以 k 点为划分为根节点进行分析,枚举 k 可以划分的可能性,
状态表示 : f[l][r] 表示的是 中序遍历的 所有二叉树的最大得分
状态计算 : f[l][r] = f[l][k-1] * f[k+1][r] + w[k]; (k表示划分的根节点)
其实就是找到该区间内最小值为根节点
#include<iostream>
using i64 = long long;
const int N = 55;
int f[N][N], g[N][N]; // f[N][N]表示我们的状态 ,g[N][N] 表示 存储前序遍历的划分的根节点
int n; //节点的个数
int w[N]; //存储每个节点的分数
void dfs(int l , int r) {
if (l > r)
return;
int root = g[l][r]; //获取当前区间的划分的根节点
std::cout << root << " ";
dfs(l, root - 1);
dfs(root + 1, r);
}
void solve() {
std::cin >> n;
for (int i = 1; i <= n; i++) std::cin >> w[i];
for (int len = 1; len <= n; len++) { //区间的长度(二叉树的长度)
for (int l = 1; len + l - 1 <= n; l++) { //区间的起点
int r = len + l - 1;
if (len == 1) { // 边界情况 只有一个节点,叶子节点,
f[l][r] = w[l];
g[l][r] = l; // 划分的根节点,即就是叶子节点自己
} else {
for (int k = l; k <= r ; k++) { //注意 k = l 而不是 l+1, k是可以为根节点的
// 所以 k是可以为l 或 r的
int left = k == l ? 1 : f[l][k - 1]; // 左边的节点的分数
int right = k == r ? 1 : f[k + 1][r]; // 右边节点的分数
i64 score = left * right + w[k];// f[l][k-1] * f[k+1][r] + w[k];
// 也就是 这个根和下面节点的分数
if (score > f[l][r]) {
f[l][r] = score;
g[l][r] = k; // 若分数大于f[l][r] 说明此时k点划分为根节点时,分数最大。
}
}
}
}
}
std::cout << f[1][n] << '\n';
dfs(1, n); //遍历路径
}
int main() {
solve();
return 0;
}