题意:
给定一棵含 个节点的二叉树,其节点编号为 到 。二叉树的中序遍历为 。每个节点有一个分数 。
任一棵子树subtree(也包含tree本身)的加分计算方法为:
subtree的左子树的加分 subtree的右子树的加分+subtree 的根的分数。
若左右子树中某一子树为空,规定其分数为 1。
若左右子树都为空(即叶节点),则分数为叶节点本身的分数。
求分树最高的 tree。
解法:
对二叉树的概念和二叉树的中序遍历有一定了解的话,就很容易得到这样的性质:
二叉树的中序遍历可以不断递归划分成:(左子树)根节点(右子树)。
比如样例输出的那棵二叉树:
根节点为 ,则可以划分成 。
根节点为 ,则划分成 。
根节点为 ,则划分成 。
有了这个最关键的性质中序遍历不断递归划分成(左子树)根节点(右子树)这样的结构。
那么这道题的解法也很自然的出来了:区间 dp。
表示编号从 到 构成的二叉树的分数的最大值。
枚举根节点 即可更新 。
再考虑一下边界条件,就可以按题意写出 方程:
题目需要输出二叉树的前序遍历。
可以再开一个数组,用 表示编号从 到 构成的二叉树的根节点编号。
在 转移的时候记录一下就可以了。
最后 dfs 输出前序遍历。
Code:
#include <bits/stdc++.h> using namespace std; const int N = 35; using uint = unsigned int; uint dp[N][N], pre[N][N], a[N], n; void dfs(int l, int r) { if(l > r) return; int k = pre[l][r]; cout << k << " "; dfs(l, k - 1); dfs(k + 1, r); } int main() { for(int i = 0; i < N; i++) for(int j = 0; j < N; j++) dp[i][j] = 1; cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) { dp[i][i] = a[i]; pre[i][i] = i; } for(int len = 2; len <= n; len++) for(int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; dp[i][j] = 0; for(int k = i; k <= j; k++) { uint v = dp[i][k - 1] * dp[k + 1][j] + a[k]; if(dp[i][j] < v) { dp[i][j] = v; pre[i][j] = k; } } } cout << dp[1][n] << endl; dfs(1, n); return 0; }