P1040 加分二叉树(树形dp)

加分二叉树

题目传送门

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

输入格式
第 1 行 1 个整数 n,为节点个数。 第 2 行 n 个用空格隔开的整数,为每个节点的分数

输出格式
第 1 行 1 个整数,为最高加分(Ans≤4,000,000,000)。 第 2 行 n 个用空格隔开的整数,为该树的前序遍历。

输入输出样例
输入

5 
5 7 1 2 10

输出

 145 
 3 1 2 4 5 

说明/提示 n<30。 分数 <100。

解题思路

第一问(最高加分): 定义一个f[i][j]表示从i到j的最大值,则f[i][j]=max(f[i][k-1]*f[k+1][j]+a[k]),枚举k
第二问(前序遍历): 只有把树建好了,才能输出其前序遍历。于是,我们看到了两个关键词:二叉树,中序遍历。有了这两个关键词,加上区间动规,这棵树就能建起来了

AC代码

#include
#include
using namespace std;
long long n,l,r,t,b[35],f[35][35],father[35][35];//father[i][j]=k表示i到j的根为k
void sc(int x,int y)//输出(前序遍历)
{
	if(father[x][y]!=0)cout<<father[x][y]<<' ';
	if(father[x][father[x][y]-1]!=0)sc(x,father[x][y]-1);//递归
	if(father[father[x][y]+1][y]!=0)sc(father[x][y]+1,y);
	return;
}
int main()
{
	scanf("%lld",&n);
	for(int i=0;i<=n;i++)//初值
	 for(int j=0;j<=n;j++)
	  f[i][j]=1;
	for(int i=1;i<=n;i++)//输入
	{
		scanf("%lld",&b[i]);
		f[i][i]=b[i];//初值
		father[i][i]=i;
	} 
	for(int i=1;i<=n;i++)//区间的长度
	 for(int l=1;l<=n;l++)//区间的左端点
	 {
	 	r=l+i;//区间的右端点
	 	if(r<=n)//如果右端点超出n,就不能下面的程序
	 	{
	 		t=-2147483647;
	 		for(int k=l;k<=r;k++)//若根节点的下标是k,则左端点的是k-1,右端点是k+1
	 		 if(t<f[l][k-1]*f[k+1][r]+b[k])//找最大值
	 		 {
	 		 	t=f[l][k-1]*f[k+1][r]+b[k];
	 		 	father[l][r]=k;
	 		 }
	 		f[l][r]=t; //赋值
	 	}
	 }
	cout<<f[1][n]<<endl;//输出
	sc(1,n);
}

谢谢

你可能感兴趣的:(动规dp,树形dp)