479. 加分二叉树
题目链接
479. 加分二叉树
设一个 \(n\) 个节点的二叉树 tree 的中序遍历为\((1,2,3,…,n)\),其中数字 \(1,2,3,…,n\) 为节点编号。
每个节点都有一个分数(均为正整数),记第 \(i\) 个节点的分数为 \(d_i\),tree 及它的每个子树都有一个加分,任一棵子树 subtree(也包含 tree 本身)的加分计算方法如下:
subtree的左子树的加分 × subtree的右子树的加分 + subtree的根的分数
若某个子树为空,规定其加分为 \(1\)。
叶子的加分就是叶节点本身的分数,不考虑它的空子树。
试求一棵符合中序遍历为\((1,2,3,…,n)\)且加分最高的二叉树 tree。
要求输出:
(1)tree的最高加分
(2)tree的前序遍历
输入格式
第 \(1\) 行:一个整数 \(n\),为节点个数。
第 \(2\) 行:\(n\) 个用空格隔开的整数,为每个节点的分数(0<分数<100)。
输出格式
第 \(1\) 行:一个整数,为最高加分(结果不会超过int范围)。
第 \(2\) 行:\(n\) 个用空格隔开的整数,为该树的前序遍历。如果存在多种方案,则输出字典序最小的方案。
数据范围
\(n<30\)
输入样例:
5
5 7 1 2 10
输出样例:
145
3 1 2 4 5
解题思路
区间dp
-
状态表示:\(f[l][r]\) 表示中序遍历中区间 \([l,r]\) 内组成的树的最高加分
-
状态计算:\(f[l][r]=max(f[l][r],f[l][k-1]*f[k+1][r]+w[k]\),其中 \(k\) 为该区间组成的树
带着区间和权值反着推即可得整棵树的结构
- 时间复杂度:\(O(n^2)\)
代码
// Problem: 加分二叉树
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/481/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=35;
int n,w[N],f[N][N];
void dfs(int x,int l,int r)
{
for(int i=l;i<=r;i++)
if(i==l)
{
if(x==f[i+1][r]+w[i])
{
cout<<i<<' ';
dfs(f[i+1][r],i+1,r);
return ;
}
}
else if(i==r)
{
if(x==f[l][i-1]+w[i])
{
cout<<i<<' ';
dfs(f[l][i-1],l,i-1);
return ;
}
}
else if(x==f[l][i-1]*f[i+1][r]+w[i])
{
cout<<i<<' ';
dfs(f[l][i-1],l,i-1);
dfs(f[i+1][r],i+1,r);
return ;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++)f[i][i]=w[i];
for(int len=2;len<=n;len++)
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
for(int k=l;k<=r;k++)
{
if(k==l)
f[l][r]=max(f[l][r],f[k+1][r]+w[k]);
else if(k==r)
f[l][r]=max(f[l][r],f[l][k-1]+w[k]);
else
f[l][r]=max(f[l][r],f[l][k-1]*f[k+1][r]+w[k]);
}
}
cout<<f[1][n]<<'\n';
dfs(f[1][n],1,n);
return 0;
}