[NOIP2003]加分二叉树

嘟嘟嘟

 

中序遍历有一个特点:序列[1, n]是一个中序遍历,i 是根节点,则[1, i - 1]是他的左子树的中序遍历,[i + 1, n]为右子树的中序遍历。所以就想到了区间dp,对于[i, j]枚举根节点k,则dp[i][j] = max(dp[i][k - 1] * dp[k + 1][j] + a[k])。初始化dp[i][i] = a[i],又因为空节点为1,所以dp[i][i - 1] = 1。

前序遍历的话就再开一个二维数组f[i][j]存这个区间所属哪一个节点,输出的时候跟区间dp很像,具体看代码吧。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 35;
21 inline ll read()
22 {
23   ll ans = 0;
24   char ch = getchar(), last = ' ';
25   while(!isdigit(ch)) {last = ch; ch = getchar();}
26   while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
27   if(last == '-') ans = -ans;
28   return ans;
29 }
30 inline void write(ll x)
31 {
32   if(x < 0) x = -x, putchar('-');
33   if(x >= 10) write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 
37 int n, a[maxn];
38 ll dp[maxn][maxn];
39 int f[maxn][maxn];
40 
41 void print(int L, int R)
42 {
43   if(R < L) return;
44   if(L == R) {write(L), space; return;}
45   write(f[L][R]), space;
46   print(L, f[L][R] - 1);
47   print(f[L][R] + 1, R);
48 }
49 
50 int main()
51 {
52   n = read();
53   for(int i = 1; i <= n; ++i) a[i] = read();
54   for(int i = 1; i <= n; ++i) dp[i][i] = a[i], dp[i][i - 1] = 1;
55   for(int L = 2; L <= n; ++L)
56     for(int i = 1; i + L - 1 <= n; ++i)
57       {
58     int j = i + L - 1;
59     for(int k = i; k <= j; ++k)
60       {
61         ll Max = dp[i][k - 1] * dp[k + 1][j] + a[k];
62         if(Max > dp[i][j]) dp[i][j] = Max, f[i][j] = k;
63       }
64       }
65   write(dp[1][n]); enter;
66   print(1, n);
67   return 0;
68 }
View Code

 

posted @ 2018-10-18 10:53  mrclr  阅读(149)  评论(0编辑  收藏  举报