动态规划:P1040[NOIP2003 提高组] 加分二叉树 树形DP

P1040[NOIP2003 提高组] 加分二叉树
题目:

 

思路:

  题目给的是中序遍历,所以一定是根左右,在序列中,对于每一个结点,他左边的数字可能就是他的左结点,或者左子树为空,右边的数字可能就是他的右结点,或者右子树为空,右边的数字跟他没关系。所以我们可以先看每一个点,对于每一个点来说,以他自己为一棵树,左子树右子树均为空的加分就是他的点权,可以通过题目的加分计算方法得知。我们先构建dp[i][j] 数组 dpi j 就代表以i为树的最左边的点,j为树的最右边的点构造树,因为是中序遍历 左根右。由刚刚推导的一个点为树的情况,dp[i][i]就都等于点权,所以初始化的时候,不需要再构建点权数组,直接初始化在dp[i][i],然后我们寻求状态转移,对于下一个状态,也就是树里面只有两个点的情况,我们的思路就是从树只有一个点推到树序列的总长度,有点吧这一题转化为区间DP了,因为他给的中序序列构造出来的树是任意的,无法用构造树以后去做。那么对于区间长度为2的dp[l][r],先初始化为dp[l+1][r]+dp[l][l];//默认左子树为空,然后进行切割,也就是对于这个区间,在中间选取一个结点做根,因为是中序遍历,根左右,但是对于长度为2的点,无法切割,选根其实只能从长度为3的开始,因为根左右,主要是长度为2的点没必要切割,他默认左树为空和右子树为空,和初始化的值是一样的,切割也就这两种情况,没有必要,然后就从2一直推到n,但是要求前序遍历,根左右,每一次切割找到更好地根就记录root[i][j]=k k就是最好的根,我们构建这个root就是存区间为i j 时最优的根k,然后递归寻找路径,或者前序序列。构造一个find函数,每次传入区间的l and r,输出根root [l][r],因为是前序序列,所以再find中再递归调用find查找左区间和有区间,find l k-1,and find k+1 r  注意递归返回条件 当l==r 时,就输出l 因为是以自己为根 这就是叶子结点,l>r 就直接return,边界条件。

  关键DP代码:

 

 

 

find函数:

 

 

完整AC代码:

 1 #include<iostream>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn = 50;
 5 ll dp[maxn][maxn];
 6 int root[maxn][maxn];
 7 int n;
 8 void find(int l,int r)
 9 {
10     if (l == r)
11     {
12         cout << root[l][r]<<" ";
13         return;
14     }
15     if (l > r)
16         return;
17     int head = root[l][r];
18     cout << head << " ";
19     find(l, head - 1);
20     find(head+1,r );
21 }
22 int main()
23 {
24     cin >> n;
25     for (int i = 1; i <= n; ++i)
26     {
27         cin >> dp[i][i];
28         root[i][i] = i;
29         // dp[i][i-1]=1;
30     }
31     for (int len = 1; len <= n; ++len)
32     {
33         for (int l = 1; l + len - 1 <= n; ++l)
34         {
35             int r = l + len - 1;
36             dp[l][r] = dp[l + 1][r] + dp[l][l];//默认左子树为空
37             root[l][r] = l;
38             for (int k = l + 1; k <= r - 1; ++k)//对于这个循环 只能三个以上的开始切割
39             {
40                 if (dp[l][r] < dp[l][k - 1] * dp[k + 1][r] + dp[k][k])
41                 {
42                     dp[l][r] = dp[l][k - 1] * dp[k + 1][r] + dp[k][k];
43                     root[l][r] = k;
44                 }
45                
46             }
47         }
48     }
49     cout << dp[1][n]<<endl;
50     find(1, n);
51     return 0;
52 }

 

 

 

  

 

posted @ 2022-05-05 10:21  朱朱成  阅读(73)  评论(0编辑  收藏  举报