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

AcWing 479. 加分二叉树(区间dp)

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

题目链接

设一个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])

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

答案:

#include <iostream>
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f3f3f3f3f
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define pb push_back
#define eps 1e-6
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
const int M = 35;
using namespace std;

int n;
ll dp[M];
ll mp[M][M];
ll vp[M][M];

void DFS(int l,int r){
    if(l>r) return ;
    int root=vp[l][r];
    cout<<root<<" ";
    DFS(l,root-1);
    DFS(root+1,r);
}

void solve(){;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>dp[i];
    }
    for(int i=1;i<=n;i++){
        mp[i][i]=dp[i];
        vp[i][i]=i;
    }
    for(int i=2;i<=n;i++){
        for(int j=1;i+j-1<=n;j++){
            int key=i+j-1;
            for(int k=j;k<=key;k++){
                int l=(j==k?1:mp[j][k-1]);
                int r=(key==k?1:mp[k+1][key]);
                int res=l*r+dp[k];
                if(res>mp[j][key]){
                    mp[j][key]=res;
                    vp[j][key]=k;
                }
            }
        }
    }
    cout<<mp[1][n]<<endl;
    DFS(1,n);
    cout<<endl;
}

int main()
{
    solve();
    return 0;
}