zoukankan      html  css  js  c++  java
  • 基础树形DP——P1040 加分二叉树

    题目描述

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

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

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

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

    (1)tree的最高加分

    (2)tree的前序遍历

    输入格式

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

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

    输出格式

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

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

    解法

    这个题目的一个重要条件是你求解的二叉树的中序遍历是(1,2,3...n)。在二叉树的中序遍历中,对于任意一个点i,它前面的数一定都在它的左子树中, 它右边的数可能在它的右子树中(挺显然的吧)。因此中序遍历区间[1,i]一定是以某个(a_j)(j∈[1,i])为根的子树(还算显然吧).

    因此我们可以对于每一段区间试着选出一个根,然后算出以它为根的最大值,最后记录答案.

    具体来说:

    (f_{i,j})表示以点([i,j])组成一棵二叉树的最大加分,接下来我们来枚举断点k.这个断点事实上代表了树根,即我们令(i)(k-1)是左子树,(k+1)(j)是右子树,然后我们递归下去,退回以后更新答案,枚举下一个(k).最终目标:(f_{1,n}).过程中如果记录过答案就直接返回记录的值,也就是应用树形DP常用的做法:记忆化搜索.

    代码:

    int dfs(int l, int r) {
        if (l > r) return 1;//不合法判断
        if (l == r) {//递归到叶子节点
            root[l][r] = l;//以它自己为根
            return val[l];
        }
        if (maxinum[l][r]) return maxinum[l][r];//记忆化的关键
        int k, ans = -1000000000;
        for (int i = l; i <= r; ++i) {
            int temp = dfs(l, i - 1) * dfs(i + 1, r) + val[i];//枚举了断点,递归下去更新答案
            if (temp > ans) {//更新答案
                ans = temp;
                k = i;
            }
        }
        root[l][r] = k;//记录最合适的根
        maxinum[l][r] = ans;//记录最大答案
        return ans;
    }
    

    这样我们就做完了第一问

    别忘了,这题还有第二问[允悲]
    第二问要求我们输出前序遍历,也就是先输出根,后输出左儿子,后输出右儿子.其实我们在第一问中已经得到了这个问题的答案...左儿子,也就是左子树的根,右儿子,也就是右子树的根.我们选完根,第二问就已经得到答案了.我们只需要从(root_{1,n})开始,先递归(root_{1,root_{1,n}-1})再递归(root_{root_{1,n}+1,n}),然后以此类推就可以了.

    可能上面说的有点绕,具体看代码吧:

    void findroot(int l, int r) {
        if (l > r) return;
        cout << root[l][r] << " ";
        findroot(l, root[l][r] - 1);
        findroot(root[l][r] + 1, r);
        return;
    }
    

    是不是很简短呢?

    哦对了,别忘了开long long.

  • 相关阅读:
    移动端hybrid开发复盘
    node/webpack 调试 loader 等技巧
    javascript(js)小数精度丢失的解决方案
    必经之路--买房之后需要走的流程--针对 组合贷款方式
    canvas 画半圆的两种方式
    svg path 画圆
    1.快速排序
    7.桥接设计模式
    6.适配器设计模式
    5.策略设计模式
  • 原文地址:https://www.cnblogs.com/i-cookie/p/11383913.html
Copyright © 2011-2022 走看看