加分二叉树(区间DP,树形DP)

设一个nn个节点的二叉树tree的中序遍历为(1,2,3,…,n1,2,3,…,n),其中数字1,2,3,…,n1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,treedi,tree及它的每个子树都有一个加分,任一棵子树subtreesubtree(也包含treetree本身)的加分计算方法如下:

subtreesubtree的左子树的加分× subtreesubtree的右子树的加分+subtreesubtree的根的分数。

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

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

(1)treetree的最高加分

(2)treetree的前序遍历

输入格式

第11行:11个整数n(n<30)n(n<30),为节点个数。

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

输出格式

第11行:11个整数,为最高加分(Ans \le 4,000,000,000≤4,000,000,000)。

第22行:nn个用空格隔开的整数,为该树的前序遍历。

输入输出样例

输入 #1复制

5
5 7 1 2 10

输出 #1复制

145
3 1 2 4 5

思路:

  1. 中序遍历的树根左边是左子树,右边是右子树
  2. 在子树中序遍历序列中,依旧为一个树根,左边是左子树,右边是右子树
  3. 最大加分二叉树的子树也为最大加分二叉树
  4. 因此可将其视为区间DP,并得出递推式:dp[i][j]=max(dp[i][j],dp[i][m-1]*dp[m+1][j]+dp[m][m]);   dp[i][j]表示序列i ~j可形成的最大加分二叉树,m为枚举的根序号,范围i~j
#include<iostream>
#include<algorithm>
using namespace std;
int n,mid[32][32]={0},dp[32][32];
void first(int x,int y){  //先序遍历区间X~Y,并输出序列 
	if(x==y){
		cout<<" "<<x;
		return;
	}
	if(y<x)return ;
	cout<<" "<<mid[x][y];
    first(x,mid[x][y]-1);
    first(mid[x][y]+1,y);
}
int main() {
	cin>>n;
	for(int i=0; i<=n; i++)
		fill(dp[i],dp[i]+n+2,1);
	for(int i=1; i<=n; i++)
		cin>>dp[i][i],mid[i][i]=i;
	for(int k=1; k<n; k++) {   //枚举区间大小 ,k表示间隔大小 
		for(int i=1,j=i+k; j<=n; i++,j++) {  //枚举区间起始和终点 
			for(int m=i; m<=j; m++) {  //枚举树根 
				int t=dp[i][m-1]*dp[m+1][j]+dp[m][m];  //递推式 
				if(dp[i][j]<t){
					dp[i][j]=t;
					mid[i][j]=m;  //存储树根 
				}
			}
		}
	}cout<<dp[1][n]<<endl;  //区间i到n的最大加分二叉树大小 
	cout<<mid[1][n];//输出区间1~n的树根 
	first(1,mid[1][n]-1);  //输出区间1~n-1的所有树根 
	first(mid[1][n]+1,n);
	return 0;
}
发布了42 篇原创文章 · 获赞 16 · 访问量 3403

猜你喜欢

转载自blog.csdn.net/qq_41542638/article/details/99656008