NC16681 [NOIP2003]加分二叉树
题目
题目描述
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。 试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。
要求输出:
(1)tree的最高加分
(2)tree的前序遍历
输入描述
第1行:一个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
输出描述
第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
示例1
输入
5
5 7 1 2 10
输出
145
3 1 2 4 5
题解
知识点:DFS,树,区间dp。
因为给出的是中序遍历,因此一个区间可以作为一个树,两个区间加一个中间的点可以作为做左子树根节点和右子树,于是可以区间dp。
设 \(dp[i][j]\) 为区间 \([i,j]\) 构成树后的最高分,有转移方程:
当 \(k = i \text{ or } j\) 时,表示的是空树,应该用 \(1\) 代替。
可以边dp边记录某个树 \([i,j]\) 的父节点 \(fa[i][j]\) ,因为 \(k\) 本身是作为根节点划分区间。
最后dfs一下 \(fa[1][n]\) 即可。
时间复杂度 \(O(n^3)\)
空间复杂度 \(O(n^2)\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[37], fa[37][37];
ll dp[37][37];
void dfs(int l, int r) {
if (l > r) return;
cout << fa[l][r] << ' ';
dfs(l, fa[l][r] - 1);
dfs(fa[l][r] + 1, r);
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i], dp[i][i] = a[i], fa[i][i] = i;
for (int l = 2;l <= n;l++) {
for (int i = 1, j = l;j <= n;i++, j++) {
for (int k = i;k <= j;k++) {
ll t = max(1LL, dp[i][k - 1]) * max(1LL, dp[k + 1][j]) + a[k];
if (dp[i][j] < t) {
dp[i][j] = t;
fa[i][j] = k;
}
}
}
}
cout << dp[1][n] << '\n';
dfs(1, n);
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16589644.html