题目描述
设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,treedi,tree及它的每个子树都有一个加分,任一棵子树subtreesubtree(也包含treetree本身)的加分计算方法如下:
subtreesubtree的左子树的加分× subtreesubtree的右子树的加分+subtreesubtree的根的分数。
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树treetree。要求输出;
(1)treetree的最高加分
(2)treetree的前序遍历
输入输出格式
输入格式:
第1行:1个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
输出格式:
第1行:1个整数,为最高加分(Ans≤4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
输入输出样例
输出样例#1:
145 3 1 2 4 5
解题思路:
一道区间DP题,题目给了树的中序遍历,那么我们可以知道每个节点的左边肯定是它的左子树,右边肯定是它的右子树,那么我们就可以用一个数组f[i][x]表示i到x区间的子树最大值,设置一个断点j,枚举区间内的根节点,由此结合题意中的一个子树和=左子树和*右子树和+根节点,得出状态状态转移方程
f[i][x] = max(f[i][x],f[i][j-1]*f[j+1][x]+f[j][j]),在状态转移时,记录根节点,以便后面输出前序遍历.
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 long long n,f[35][35],gen[35][35]; 4 void dfs(int l,int r) {//递归求先序遍历 5 if(l > r) return ; 6 if(l == r) { 7 cout << gen[l][r] << ' '; 8 return ; 9 } 10 cout << gen[l][r] << ' '; 11 dfs(l, gen[l][r] - 1); 12 dfs(gen[l][r] + 1, r); 13 } 14 int main() 15 { 16 cin >> n; 17 for(int i = 1;i <= n; i++) { 18 cin >> f[i][i]; 19 f[i][i-1] = 1; 20 gen[i][i] = i;//初始化每个节点的根为自己 21 } 22 for(int i = n;i >= 1; i--) 23 for(int x = i + 1;x <= n; x++) 24 for(int j = i;j <= x; j++) 25 if(f[i][j-1]*f[j+1][x]+f[j][j] > f[i][x]) { 26 f[i][x] = f[i][j-1]*f[j+1][x]+f[j][j];//状态转移 27 gen[i][x] = j;//记录根节点 28 } 29 cout << f[1][n] <<endl;//求1到n的最大和 30 dfs(1,n); 31 32 33 return 0; 34 }
//NOIP提高 2003 T3