• 【NOIP2013提高组T3】加分二叉树


    题目描述

    设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为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个用空格隔开的整数,为该树的前序遍历。

    输入输出样例

    输入样例#1

    5

    5 7 1 2 10

    输出样例#1

    145

    3 1 2 4 5

    算法:

    区间DP

     

    分析:

    这个10多年前的提高组试题其实也不算太难,但是有很多要注意的小点。

    首先这道题上手先分析感觉和树形DP有点关系,然而再看清楚一点呢,就发现它其实只是在区间上dp,然而树只是对它的一个约束。

    我们先来简化题目,假如我问的是在一个区间上求最大加分,那么这个状态转移方程应该很容易得到,就是f[i][j]=max(f[i][k-1]*f[k+1][j]+a[k])其中表示从i到j的区间最大值,k满足i<=k<=j。

    注意这里可以取等。

     

    接下来,有一个性质:

    对于任意二叉树,其中序遍历中的任意一段区间的根节点可以是任何一个节点。

     

    那么问题解决。

     

    这个就可以直接套用到区间DP中,不过还要记录下相应的i,j的根节点是什么。最后输出先序遍历的过程其实就是dfs的思想,加一个记忆化搜索会提高效率。

     

    要点注意:

    1、为了方便调试,可以将数组开到6,但注意要调回正常值在提交

    2、存答案的数组要用long long,否则会wa

    3、记忆化部分要确定有值(非空子树)才递归下去

    4、初始化答案数组f要放在输入前

    5、注意要先定好某次递推的区间长度,否则会找到没运算过的值。

    上代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 
     5 long long f[35][35];                            //注意要用超长整型
     6 int n,r[35][35],a[35],fl=0;
     7 
     8 inline int read()
     9 {
    10     int x=0,f=1;
    11     char c=getchar();
    12     while (c<48||c>57)
    13         f=c=='-'?-1:1,c=getchar();
    14     while (c>=48&&c<=57)
    15         x=(x<<1)+(x<<3)+(c^48),c=getchar();
    16     return x*f;
    17 }
    18 
    19 void tree(int lt,int rt)
    20 {
    21     if (r[lt][rt])                            //某些oj会判结尾为空格的情况
    22     {
    23         if (!fl)
    24             printf("%d",r[lt][rt]),fl=1;
    25         else
    26             printf(" %d",r[lt][rt]);
    27     }
    28     if (r[lt][r[lt][rt]-1])                        //递归左子树
    29         tree(lt,r[lt][rt]-1);
    30     if (r[r[lt][rt]+1][rt])                        //递归右子树
    31         tree(r[lt][rt]+1,rt);
    32 }
    33 
    34 int main()
    35 {
    36     int i,j,k,len;
    37     n=read();
    38     for (i=0;i<=n;i++)                        //初始化为1,注意不可以memset
    39         for (j=0;j<=n;j++)
    40             f[i][j]=1;
    41     for (i=1;i<=n;i++)
    42     {
    43         a[i]=read();
    44         f[i][i]=a[i];                        //每个点自己作为叶子节点时,
    45         r[i][i]=i;                            //根节点就是自己
    46     }
    47     /*                                //注意这种方法不可取
    48     for (i=1;i<=n-1;i++)
    49         for (j=i+1;j<=n;j++)
    50         {
    51             long long tmp=INF;
    52             for (k=i;k<=j;k++)
    53                 if (tmp<f[i][k-1]*f[k+1][j]+a[k])
    54                 {
    55                     tmp=f[i][k-1]*f[k+1][j]+a[k];
    56                     r[i][j]=k;
    57                 }
    58             f[i][j]=tmp;
    59         }
    60     */
    61     for (len=1;len<=n;len++)                        //先定区间长度
    62         for (i=1;i+len<=n;i++)                    //设起点,i+len为终点
    63         {
    64             long long tmp=-1;                    //没必要太小
    65             for (k=i;k<=i+len;k++)                //寻根
    66                 if (tmp<f[i][k-1]*f[k+1][i+len]+a[k])
    67                 {
    68                     tmp=f[i][k-1]*f[k+1][i+len]+a[k];
    69                     r[i][i+len]=k;
    70                 }
    71             f[i][i+len]=tmp;
    72         }
    73     printf("%lld
    ",f[1][n]);
    74     tree(1,n);
    75     return 0;
    76 }

    讲讲memset为什么不行,因为在c++中memset是按位来赋值的,一个int是4位,一个long long是8位(好像是吧),所以一次就会推了8个1,而不是想要的一个1。而对于清零和赋极值memset是很好用的。

     

    嗯,就这样了。

  • 相关阅读:
    15款经典图表软件推荐 创建最漂亮的图表
    CSS+JS打造的自适应宽度的滑动门和选项卡
    兼容多浏览器的加入收藏代码
    指针与引用深层次的区别
    反编译winform资源文件
    程序创业必过三关
    自动ping博客服务程序
    C#批量加水印程序
    C#应用程序随机启动
    失败降临是命中注定
  • 原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/8445336.html
Copyright © 2020-2023  润新知