加分二叉树

原题链接:https://www.luogu.org/problem/show?pid=1040

刚拿到这道题的我是懵逼的,因为刚开始除了暴力我完全想不到从哪入手。

其实这题是一个区间dp和dfs,代码还是挺简单的。

参考了一篇题解,对那位大佬表示由衷的敬意。

设f[i][j]表示在区间(i,j)内的最大加分,那么有f[i][j] = max(f[i][k-1]*f[k+1][j]+a[k]) ,a[k]代表第i个点的分数,k是枚举的根节点,即状态转移方程的那个式子是指的在区间(i,j)上的以k为根节点的最大分数。

还记得区间dp的基本思路吗?由小区间推出大区间,这题也是这样,i从n倒推至1,j从i正推至n,而中间的枚举根节点k的范围是[i,j]。

同时我们还需要一个数组n[i][j]表示在区间(i,j)加分最大时的根节点,在后面会用到。

最高加分便是f[1][N]。//为了不和n数组重复用一个大写

考虑下一个问题,如何输出一棵树的前序遍历?

还记得什么是前序遍历吗?先根,再左子树,再右子树。

要输出一个遍历,递归输出就好。首先输出当前的点(根节点),然后输出左子树,再输出右子树,简单的划分。

参考代码:

 1 #include <iostream>
 2 #define maxn 35
 3 using namespace std;
 4 int a[maxn];
 5 int N;
 6 int f[maxn][maxn];
 7 int n[maxn][maxn];
 8 
 9 
10 void dfs(int l,int r){
11     if (l>r)
12         return;
13     int mid = n[l][r];
14     cout << mid << " ";
15     dfs(l,mid-1);
16     dfs(mid+1,r);
17 }
18 
19 int main(){
20     cin >> N;
21     for (int i=0;i<=N;i++)
22         for (int j=0;j<=N;j++)
23             f[i][j] = 1;
24 
25     for (int i=1;i<=N;i++){
26         cin >> a[i];
27         f[i][i] = a[i];
28         n[i][i] = i;
29     }
30 
31     for (int i=N-1;i>=1;i--)
32         for (int j=i+1;j<=N;j++)
33             for (int k=i;k<=j;k++)
34                 if (f[i][k-1]*f[k+1][j]+a[k]>f[i][j]){
35                     n[i][j]=k;
36                     f[i][j]=f[i][k-1]*f[k+1][j]+a[k];
37                 }
38     cout << f[1][N] << endl;
39     dfs(1,N);
40 
41     return 0;
42 }

 

posted @ 2017-09-24 23:48  ShawnZhou_Aether  阅读(148)  评论(0编辑  收藏  举报