欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AcWing479.加分二叉树(区间DP)题解

程序员文章站 2022-07-12 21:57:22
...

加分二叉树

题目传送门

题目描述

设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。

每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:

subtree的左子树的加分 × subtree的右子树的加分 + subtree的根的分数

若某个子树为空,规定其加分为1。叶子的加分就是叶节点本身的分数,不考虑它的空子树。

试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。

要求输出:

(1)tree的最高加分

(2)tree的前序遍历

输入格式

第1行:一个整数n,为节点个数。

第2行:n个用空格隔开的整数,为每个节点的分数(0<分数<100)。

输出格式

第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。

第2行:n个用空格隔开的整数,为该树的前序遍历。如果存在多种方案,则输出字典序最小的方案。

数据范围

n<30

输入样例:

5
5 7 1 2 10

输出样例:

145
3 1 2 4 5

题解:

区间DP同样适用于二叉树:

枚举二叉树的节点个数 len, 枚举二叉树的组成区间(l, r), 枚举根节点 k, 从而得到状态转移方程式:

f[ l ] [ r ] = max(f[ l ] [ r ], f[ l ] [ k - 1] + f[ l ] [k + 1], + w[k])

更新最大值的同时, 记录每一个树的根节点

//DP[i][j] 表示区间[i ~ j] 表示为一个二叉树时的分数的最大值
#include<iostream>
using namespace std;
const int N = 35;
int n;
int w[N];
int f[N][N], g[N][N]; // g数组用来存储每一个区间的根节点
void dfs(int l, int r, int k)
{
    if(l > r)return ;
    cout << k << ' ';
    dfs(l, k - 1, g[l][k - 1]);
    dfs(k + 1, r, g[k + 1][r]);
}
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> w[i];
    for(int len = 1; len <= n; len++){   //枚举区间长度
        for(int l = 1; l + len - 1 <= n; l++){ //枚举起点
            int r = l + len - 1;  //得到右端点
            if(len == 1){  //如果是根节点
                f[l][r] = w[l]; 
                g[l][r] = l;
            }
            else{
                for(int k = l; k <= r; k++){  //枚举根节点
                    //判断左子树是否为空
                    int left = k == l ? 1 : f[l][k - 1];  //得到左子树和右子树的分数
                    int right = k == r ? 1 : f[k + 1][r];
                    if(left * right + w[k] > f[l][r]){   //更新最大值
                        f[l][r] = left * right + w[k];
                        g[l][r] = k;  //跟新根节点
                    }
                }
            }
        }
    }
    cout << f[1][n] << endl;
    dfs(1, n, g[1][n]);
    return 0;
}
相关标签: 动态规划