【基础练习】【区间DP】codevs1090 加分二叉树题解

2003 NOIP TG

题目描写叙述 Description

设一个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的前序遍历

如今,请你帮助你的好朋友XZ设计一个程序。求得正确的答案。

输入描写叙述 Input Description

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

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

输出描写叙述 Output Description

第1行:一个整数。为最高加分(结果不会超过4,000,000,000)。

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

例子输入 Sample Input

5

5 7 1 2 10

例子输出 Sample Output

145

3 1 2 4 5

数据范围及提示 Data Size & Hint

n(n<=30)

分数<=100

定义f[i,j]表示中序遍历序列为i,…,j的树的最高得分。

枚举根结点k:i<=k<=j。得到不同的二叉树:

左子树中序遍历序列为i,…,k-1,最大加分是f[i,k-1]。

右子树中序遍历序列为k+1,…,j,最大加分是f[k+1,j]。

状态转移方程:

f[i,j]=max{f[i,k-1]*f[k+1,j]+a[k]}  (i<k<j)。

初始:

f[i,i]=a[i];  f[i,j]=1;

目标:f[1,n]。

在求f[i,j]的同一时候,记下i,…,j的根k,root[i,j]:=k;

这道题有非常多细节要注意,先上代码再解释

1.初始化

首先要注意的是。两重循环的赋初值一定要在叶子节点赋初值之前,并且循环是从0開始的!

例如以下

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("%d",&a[i]);
        f[i][i]=a[i];
        root[i][i]=i;
    }

这样,在保证了其它节点f值为1的前提下,我们将叶子节点值赋值为本身分数,顺便将每一个节点的根设为他自己。

为什么要有这样两步呢?请看以下一段:

f[i][j]=0;
            for (int k=i;k<=j;k++)
            {
                if (f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k])
                {
                    f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
                    root[i][j]=k;
                }
            }

这里k是枚举i到j这棵子树根节点的变量。其值取遍i到j 假设不能取到i或j 就会导致漏方案的情况。

举个样例,我们这样写:

for (int k=i;k<j;k++)

也就是k取不到j 这样最后一个k就是j-1 此时状态为f[i][j-2]*f[j][j]+f[j-1][j-1]

好像这正是最后一种情况?不,不是。别忘了。j本身也可能是根节点

但假设j是根节点。好像又有问题。

由于这时的状态是f[i][j-1]*f[j+1][j]+f[j][j]

出现了f[j+1][j],这样的情况不可能存在!

考虑一下实际中这样的情况,相当于j节点仅仅有左子树。没有右子树,那么他的分数应该为左子树*1+他本身分数

于是我们的解决方式就出来了,初始时把全部f值赋值为1就可以

那么这个赋初值的循环为什么是从0開始呢?考虑k=1的情况,不难发现下标出现了0

另一点须要注意的是,f[i][j]在递推開始的时候,又又一次被赋值为0。

这样就保证了根节点记录的准确。

假设加分恰好是1,root中就不会记录正确的根结点了。

为什么每一个节点的根要初始化为自身呢?来看这一段:

void find(int i,int j)
{
     if (i<=j)
     {
         printf("%d ",root[i][j]);
         find(i,root[i][j]-1);
         find(root[i][j]+1,j);
     }
}

原来。一直会递归到i==j。这时输出的是叶子节点。也就是该节点本身。

又为什么须要加上if(i<=j)呢?假设不加的话。当我们递归到i==j的情况下, 下一层是root[i][i-1] 显然不能成立。因此应该及时返回。

如今。整个程序的代码基本解释完了。不如就放上证明人家区间DP身份灵魂代码吧:

for (int p=1;p<=n-1;p++)
    {
        for (int i=1;i<=n-p;i++)
        {
            int j=i+p;
            f[i][j]=0;
            for (int k=i;k<=j;k++)
            {
                if (f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k])
                {
                    f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
                    root[i][j]=k;
                }
            }
        }
    }

为什么是区间DP?看两重大循环就明确了。先枚举区间长度,然后枚举起点。最后在起点+长度构成的区间内探讨全部可能情况中的最优解。这就是区间DP,时间复杂度O(n3)

好了,这道题就解释到这里。

——裁剪冰绡,轻叠数重,淡著胭脂匀注

时间: 2024-10-15 14:47:54

【基础练习】【区间DP】codevs1090 加分二叉树题解的相关文章

石子合并问题(一) (基础的区间dp)

石子合并(一) 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述     有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值. 输入 有多组测试数据,输入到文件结束. 每组测试数据第一行有一个整数n,表示有n堆石子. 接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 输出 输出总代价的最小值,

CODEVS1090加分二叉树 noip提高组T32003

题目描述 Description 设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数 若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数.不考虑它的空 子树. 试求一棵符合中序遍历为

树形dp入门-加分二叉树(luogu1040)

今天学习了树形dp,确实,感受到了深深的压力...一会还得去写选课那道题... 先看题目: 首先我们看到关键字:中序遍历.既然已经给出我们分数的算法,所以我们就可以通过枚举根节点来解决问题.在每一个根节点下求最优解,且记录下每一个根节点,就恰好能完成两个任务.即我们需要写两个子程序. 看代码:(请勿直接复制粘贴) #include <stdio.h> int n,point[32],f[32][32],mumber[32][32]; long long tree(int left,int ri

[codevs1090]加分二叉树

不多说了,直接上代码 var n,m,i,j,k,x,y,l,r,t:longint; f,g:array[0..100,0..100]of longint; a:array[0..100]of longint; function max(a,b:longint):longint; begin if a>b then exit(a) else exit(b); end; procedure dfs(l,r:longint); var i,j:longint; b:boolean; begin i

HDU4283:You Are the One(区间DP)

Problem Description The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there ar

BZOJ 1068 [SCOI2007]压缩 区间DP

题意:链接 方法:区间DP 解析: MD写题解(吐槽)写到一半markdown挂了什么鬼! 要不要这样!你知道我的内心是什么样的吗! 吐槽,啊呸,写题解写到一半突然丢失了我的内心是崩溃的好吗! 来我们重新写题解(吐槽) 这道题我刚开始列了个瞎(和谐)动规(二维的裸区间) 加上乱七八糟的判断是否有M后,居然有交叉! 一定是我逻辑错误,对就是这样! 后来又是一顿瞎(和谐)搞之后,代码抽的爆炸,然后我一测,c-free挂掉- - 过了一个小时后,我选择死亡. 然后看了一眼hzw的题解. 看到那个三维之

NOIP2003TG 加分二叉树 区间DP

加分二叉树 (binary) [问题描述] 设一个 n 个节点的二叉树 tree 的中序遍历为( l,2,3,…,n ),其中数字 1,2,3,…,n 为节点编号.每个节点都有一个分数(均为正整数),记第 j 个节点的分数为 di , tree 及它的每个子树都有一个加分,任一棵子树 subtree (也包含 tree 本身)的加分计算方法如下: subtree 的左子树的加分 × subtree 的右子树的加分+ subtree 的根的分数 若某个子树为空,规定其加分为 1 ,叶子的加分就是叶

[Swust OJ 360]--加分二叉树(区间dp)

题目链接:http://acm.swust.edu.cn/problem/360/ Time limit(ms): 1000 Memory limit(kb): 65535 Description 设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下: subtree的左子树的加分×

cogs 106. [NOIP2003] 加分二叉树(区间DP)

106. [NOIP2003] 加分二叉树 ★☆   输入文件:jfecs.in   输出文件:jfecs.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 设 一个 n 个节点的二叉树 tree 的中序遍历为( l,2,3,…,n ),其中数字 1,2,3,…,n 为节点编号.每个节点都有一个分数(均为正整数),记第 j 个节点的分数为 di , tree 及它的每个子树都有一个加分,任一棵子树 subtree (也包含 tree 本身)的加分计算方法如下: su