【ACWing】479. 加分二叉树

题目地址:

https://www.acwing.com/problem/content/481/

设一个 n n n个节点的二叉树tree的中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,…,n) (1,2,3,,n),其中数字 1 , 2 , 3 , … , n 1,2,3,…,n 1,2,3,,n为节点编号。每个节点都有一个分数(均为正整数),记第 i i i个节点的分数为 d i d_i di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: s [ l ] × s [ r ] + s [ u ] s[l]\times s[r]+s[u] s[l]×s[r]+s[u] s [ l ] s[l] s[l]是subtree的左子树的加分, s [ r ] s[r] s[r]是subtree的右子树的加分, s [ u ] s[u] s[u]是subtree的根的分数。若某个子树为空,规定其加分为 1 1 1。叶子的加分就是叶节点本身的分数,不考虑它的空子树。试求一棵符合中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,…,n) (1,2,3,,n)且加分最高的二叉树tree。要求输出:(1)tree的最高加分(2)tree的前序遍历

输入格式:
1 1 1行:一个整数 n n n,为节点个数。第 2 2 2行: n n n个用空格隔开的整数,为每个节点的分数( 0 < s < 100 0<s<100 0<s<100 s s s是分数)。

输出格式:
1 1 1行:一个整数,为最高加分(结果不会超过int范围)。第 2 2 2行: n n n个用空格隔开的整数,为该树的前序遍历。如果存在多种方案,则输出字典序最小的方案。

数据范围:
n < 30 n<30 n<30

思路是动态规划。设 f [ i ] [ j ] f[i][j] f[i][j]是只考虑顶点 i ∼ j i\sim j ij的话的最大加分,那么可以按照树根是谁来分类,则有: f [ i ] [ j ] = max ⁡ i ≤ k ≤ j { f [ i ] [ k − 1 ] × f [ k + 1 ] [ j ] + a [ k ] } f[i][j]=\max_{i\le k\le j}\{f[i][k-1]\times f[k+1][j]+a[k]\} f[i][j]=ikjmax{ f[i][k1]×f[k+1][j]+a[k]}并且如果 j < i j<i j<i那么 f [ i ] [ j ] = 1 f[i][j]=1 f[i][j]=1。最后只需返回 f [ 1 ] [ n ] f[1][n] f[1][n]即可。递推的时候可以用另一个数组 g [ i ] [ j ] g[i][j] g[i][j]来存方案, g [ i ] [ j ] g[i][j] g[i][j] f [ i ] [ j ] f[i][j] f[i][j]最大的时候对应的那个 k k k(即树根),要输出方案的话,可以模仿前序遍历二叉树的方式递归的来做。代码如下:

#include <iostream>
using namespace std;

const int N = 30;
int n;
int a[N];
// f存递推答案,g存递推方案
int f[N][N], g[N][N];

// 做前序遍历
void dfs(int l, int r) {
    
    
    if (l > r) return;
    int root = g[l][r];
    cout << root << ' ';
    dfs(l, root - 1);
    dfs(root + 1, r);
}

int main() {
    
    
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[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][l] = a[l];
                g[l][l] = l;
            } else {
    
    
                for (int k = l; k <= r; k++) {
    
    
                    int x = k == l ? 1 : f[l][k - 1];
                    int y = k == r ? 1 : f[k + 1][r];
                    int s = x * y + a[k];
                    // 找到更优解的时候就更新答案,并且记录一下这个解对应的区间的树根
                    if (f[l][r] < s) {
    
    
                        f[l][r] = s;
                        g[l][r] = k;
                    }
                }
            }
        }

    cout << f[1][n] << endl;
    dfs(1, n);

    return 0;
}

时间复杂度 O ( n 3 ) O(n^3) O(n3),空间 O ( n 2 ) O(n^2) O(n2)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114609461