• 基础树形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.

  • 相关阅读:
    LightOJ1074(spfa+dfs标记负环及负环能够到达的点)
    (模板)AC自动机模板
    poj3660(floyd最短路)
    (模板)hdoj2544(最短路--bellman-ford算法&&spfa算法)
    hdoj4099(字典树+高精度)
    poj1056(字符串判断是否存在一个字符串是另一个字符串的前缀)
    hdoj1247(字典树)
    poj3630||hdoj1671(字典树)
    (模板)hdoj1251(字典树模板题)
    poj3348(求凸包面积)
  • 原文地址:https://www.cnblogs.com/i-cookie/p/11383913.html
Copyright © 2020-2023  润新知