洛谷 P1040 加分二叉树

题目描述

设一个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个整数,为最高加分(Ans4,000,000,000)。

2行:n个用空格隔开的整数,为该树的前序遍历。

输入输出样例

输入样例#1: 
5
5 7 1 2 10
输出样例#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

 



原文地址:https://www.cnblogs.com/lipeiyi520/p/10354206.html