P1040 [NOIP2003 提高组] 加分二叉树

P1040 [NOIP2003 提高组] 加分二叉树

题目描述

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

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

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

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

  1. tree 的最高加分。

  2. tree 的前序遍历。

对于全部的测试点,保证 1n<30,节点的分数是小于 100 的正整数,答案不超过 4×109

解法

因为中序遍历保证是 1n,所以可以从这里入手。中序遍历中,如果一个数是这颗子树的根,那么中序遍历的序列以这个数为分割点的左边是左子树,右边是右子树。

我们考虑区间 dp,设计状态 dpi,j 表示中序遍历中 ij 区间的组成的子树所可能得到分数的最大值。

则有状态转移方程:

dpi,j=maxk=ij{dpi,k1×dpk+1,j+dpk,k}

那么第一问就做完了。

我们可以在转移的时候加一个判断,用 rti,j 表示 ij 中取得最大值是这个区间的根的编号。

用代码表示就是:

if(dp[i][j] < dp[i][k - 1] * dp[k + 1][j] + dp[k][k]){
	dp[i][j] = dp[i][k - 1] * dp[k + 1][j] + dp[k][k];
	rt[i][j] = k;
}

题目要求输出前序遍历,则递归输出即可。

code:

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int MAXN = 35;
int n;
int val[MAXN];
int dp[MAXN][MAXN];
int rt[MAXN][MAXN];
void print(int l,int r){
	if(l > r) return;
	cout << rt[l][r] << " ";
	print(l,rt[l][r] - 1);
	print(rt[l][r] + 1,r);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	for(int i = 0;i <= MAXN - 1;i++){
		for(int j = 0;j <= MAXN - 1;j++){
			dp[i][j] = 1;
		}
	}
	for(int i = 1;i <= n;i++) cin >> val[i],dp[i][i] = val[i];
	for(int i = 1;i <= n;i++) rt[i][i] = i;+
	for(int len = 2;len <= n;len++){
		for(int i = 1;i <= n - len + 1;i++){
			int j = i + len - 1;
			for(int k = i;k <= j;k++){
				if(dp[i][j] < dp[i][k - 1] * dp[k + 1][j] + dp[k][k]){
					dp[i][j] = dp[i][k - 1] * dp[k + 1][j] + dp[k][k];
					rt[i][j] = k;
				}
			}
		}
	}
	cout << dp[1][n] << endl;
	print(1,n);
	return 0;
}
posted @   wyl123ly  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2023-10-23 [CSP-S 2023] 密码锁
点击右上角即可分享
微信分享提示