加分二叉树

时间限制: 1 Sec  内存限制: 125 MB
提交: 11  解决: 7

题目描述

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

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

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

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

 

输入

第1行:一个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。

 

输出

第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。

 

样例输入

5
5 7 1 2 10

样例输出

145
3 1 2 4 5

提示

 

来源

提高组-2003年NOIP

题目类型:

树形dp

思路:

首先,我们要做的就是设计状态,其实就是设计dp数组的含义,它要满足无后效性。关注这个 左子树*右子树+根 我只要知道左子树分数和右子树分数和根的分数(已给出),不就可以了吗?管他子树长什么样!

所以,我们f数组存的就是最大分数,怎么存呢?


我们发现:子树是一个或多个节点的集合。


那么我们可不可以开一个f[i][j],f[i][j]来表示节点i到节点j成树的最大加分呢?可以先保留这个想法(毕竟暂时也想不到更好的了)。

如果这样话,我们就来设计状态转移方程。按照刚刚的设计来说的话,我们的答案就是f[1][n]了,那么我们可以从小的子树开始,也就是len,区间长度。有了区间长度我们就要枚举区间起点,i为区间起点,然后就可以算出区间终点j。通过加分二叉树的式子我们可以知道,二叉树的分取决于谁是根,于是我们就在区间内枚举根k。

特别的,f[i][i]=a[i]f[i][i]=a[i],其中a[i]为第i个节点的分数。

因为是要求最大值,所以我们就可以设计出f[i][j]=MAX(f[i][k-1]*f[k+1][j]+f[k][k])f[i][j]=MAX(f[i][k−1]*f[k+1][j]+f[k][k])于是乎,我们就自己设计出了一个dp过程,因为是顺着来的,所以很少有不成立的。

至于输出前序遍历,我们再设计一个状态root[i][j]来表示节点i到节点j成树的最大加分所选的根节点。所以我们按照$根->左->右$的顺序递归输出即可。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = ;
typedef long long ll;
ll n;
//f[i][j]表示i到j子树的最大分数
ll f[MAXN][MAXN],root[MAXN][MAXN];
void print(ll l, ll r) {
if (l > r)return;
cout<<root[l][r]<<" ";//打印根
if (l == r)return;
print(l, root[l][r] - );//打印左子树
print(root[l][r]+,r);//打印右子树
}
int main()
{
cin>>n;
memset(f,,sizeof(f));
for(int i=;i<=n;i++)
{
cin>>f[i][i];
root[i][i]=i;
}
for(int len=;len<n;len++)//以子树的长度遍历,1个节点的子树,2个节点的子树。。。
{
for(int i=;i+len<=n;i++)
{
int j=i+len;
//根从i开始遍历,先假设i到j的子树,i为根
//不考虑它的空子树,所以左子树为空时,左子树为1,而不是0
//f[i][j]=f[i+1][j]+f[i][i];//此时i到j子树的分数
//root[i][j]=i;
//cout<<i<<"到"<<j<<" 最大为:"<<f[i][j]<<" 以"<<i<<"为根"<<endl;
for(int k=i;k<j;k++)//k为根,遍历每一个根,找到能使子树分数最大的根
{ ll zhi = f[i][k-]*f[k+][j]+f[k][k];
//不考虑它的空子树,所以左子树为空时,左子树为1,而不是0
//以右子树为空时,右子树为1,而不是0
if(f[i][k-]==)
{
zhi=f[k+][j]+f[k][k];
}
if(f[k+][j]==)
{
zhi=f[i][k-]+f[k][k];
}
if(f[i][j]<zhi)
{
f[i][j]=zhi;
root[i][j]=k;
//cout<<i<<"到"<<j<<" 最大为:"<<zhi<<" 以"<<k<<"为根"<<endl;
}
}
}
}
cout<<f[][n]<<endl;
print(, n);
return ;
}

洛谷P1040 加分二叉树(树形dp)的更多相关文章

  1. 洛谷P1040 加分二叉树(区间dp)

    P1040 加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di, ...

  2. [洛谷P1040] 加分二叉树

    洛谷题目链接:加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,-,n),其中数字1,2,3,-,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di ...

  3. [NOIP2003] 提高组 洛谷P1040 加分二叉树

    题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都 ...

  4. 洛谷 P1040 加分二叉树

    题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都 ...

  5. 洛谷P1040 加分二叉树【记忆化搜索】

    题目链接:https://www.luogu.org/problemnew/show/P1040 题意: 某一个二叉树的中序遍历是1~n,每个节点有一个分数(正整数). 二叉树的分数是左子树分数乘右子 ...

  6. 洛谷P1040 加分二叉树题解

    dp即可 \(f[i][j]\)表示i到j的加分 相当于区间dp了 #include<cstdio> using namespace std; int v[50]; int f[55][5 ...

  7. C++ 洛谷 2014 选课 from_树形DP

    洛谷 2014 选课 没学树形DP的,看一下. 首先要学会多叉树转二叉树. 树有很多种,二叉树是一种人人喜欢的数据结构,简单而且规则.但一般来说,树形动规的题目很少出现二叉树,因此将多叉树转成二叉树就 ...

  8. $loj10156/$洛谷$2016$ 战略游戏 树形$DP$

    洛谷loj Desription Bob 喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的方法.现在他有个问题. 现在他有座古城堡,古城堡的路形成一棵树.他要在这棵树的节点上放置最少数 ...

  9. [洛谷P2016] 战略游戏 (树形dp)

    战略游戏 题目描述 Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题. 他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得 ...

随机推荐

  1. KUANGBIN带你飞

    KUANGBIN带你飞 全专题整理 https://www.cnblogs.com/slzk/articles/7402292.html 专题一 简单搜索 POJ 1321 棋盘问题    //201 ...

  2. 在Design界面直接拖放控件的时候,提示AS- This view is not constrained vertically. At runtime it will jump to the left/(0,0) unless you

    AS- This view is not constrained vertically. At runtime it will jump to the left/(0,0) unless you ad ...

  3. github 和 jupyter

    放在github上的.ipynb文件可以用jupyter nbviewer来查看和分享. nbviewer首页: http://nbviewer.jupyter.org/ 输入github的名字或网址 ...

  4. linux安装postgresql

    第一步在编译安装postgresql源码的时候,需要用到以下依赖,若本机没有的话,需要提前安装依赖环境,执行以下命令:yum install gcc gcc-c++yum install zlib-d ...

  5. Codeforces1097D. Makoto and a Blackboard(数论+dp+概率期望)

    题目链接:传送门 题目大意: 给出一个整数n写在黑板上,每次操作会将黑板上的数(初始值为n)等概率随机替换成它的因子. 问k次操作之后,留在黑板上的数的期望. 要求结果对109+7取模,若结果不是整数 ...

  6. 关于java中为什么尽量把受检异常转化为非受检异常

    首先理解一下受检异常与非受检异常: 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机操作中可能遇到的异常,是一种常见的运行错误,只要程序设计的没有问题通常就不会发生.受检异常与程序的上 ...

  7. Python中的常用魔术方法介绍

    1.__init__ 初始化魔术方法 触发时机:初始化对象时触发(不是实例化触发,但是和实例化在一个操作中) 参数:至少有一个self,接收对象 返回值:无 作用:初始化对象的成员 注意:使用该方式初 ...

  8. hdu 1698 (延迟标记+区间修改+区间求和)

    In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. T ...

  9. 【OO学习】OO第一单元作业总结

    OO第一单元作业总结 在第一单元作业中,我们只做了一件事情:求导,对多项式求导,对带三角函数的表达式求导,对有括号嵌套的表达式求导.作业难度依次递增,让我们熟悉面向对象编程方法,开始从面向过程向面向对 ...

  10. 关于python的创立模块和导入

    首先,模块就是所有的.py文件,而模块的作用便是简化代码,使其看得简易. 例如这就是模块: 'a test'这是注释,并没有什么作用. 而创立模块的第一步:建立一个.py文件例如:这是上方这串代码的模 ...