luogu1040 加分二叉树
题目大意
设一个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的前序遍历
题解
本题最容易忽略的性质便是二叉树中的每一个子树的中序遍历都是一段连续的区间。所以对于一段区间,根据选区间中哪个点作为根来分类动规即可。
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdarg> using namespace std; void _printf(char *format, ...) { #ifdef _DEBUG va_list(args); va_start(args, format); vprintf(format, args); va_end(args); #endif } //------------------------------------------------------------------------- const int MAX_NODE = 35; long long F[MAX_NODE][MAX_NODE]; int RootId[MAX_NODE][MAX_NODE], Val[MAX_NODE]; int TotNode; void DP() { for (int i = 1; i <= TotNode; i++) F[i][i - 1] = F[i][i + 1] = 1; for (int i = 1; i <= TotNode; i++) { F[i][i] = Val[i]; RootId[i][i] = i; } for (int len = 2; len <= TotNode; len++) for (int i = 1; i <= TotNode - len + 1; i++) { int j = i + len - 1; for (int k = i; k <= j; k++) { if (F[i][k - 1] * F[k + 1][j] + Val[k] > F[i][j]) { F[i][j] = F[i][k - 1] * F[k + 1][j] + Val[k]; RootId[i][j] = k; } } } } void Print(int l, int r) { if (l > r) return; printf("%d ", RootId[l][r]); Print(l, RootId[l][r] - 1); Print(RootId[l][r] + 1, r); } int main() { #ifdef _DEBUG freopen("c:\\noi\\source\\input.txt", "r", stdin); #endif scanf("%d", &TotNode); for (int i = 1; i <= TotNode; i++) scanf("%d", Val + i); DP(); printf("%lld\n", F[1][TotNode]); Print(1, TotNode); return 0; }