洛谷1040 加分二叉树 区间dp

传送门:https://www.luogu.org/problem/show?pid=1040

题目描述

设一个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个用空格隔开的整数,为该树的前序遍历。

 

样例君

输入

5

5 7 1 2 10

输出

145

3 1 2 4 5

 

蒟蒻吐槽

  被机房dalao带着刷dp题,虽然蒟蒻更想悄咪咪地码一码线段树。

  言归正传,蒟蒻乍一看还以为是树形dp,结果是一道区间dp,和其他dp不太一样的地方,这里要在k循环时特判一下以k为根的子树没有左右儿子的情况。其他的,就和普通区间dp没什么区别了。特别的,也就是转移上一层状态时中间要把决策点k的空间留出来方便加上根的贡献。上代码了,对于区间dp还没有什么了解的战友,蒟蒻建议看一下这个博客,快照传送门普通传送门,因为蒟蒻在新疆,有的网站上不去,只有快照了,其他地方的战友应该可以上,所以有两个传送门。

  那个tr[i][j]的二维数组,记录的是区间l到r的根。代码有点丑。

  另,因为蒟蒻实在上不了csdn,如果侵权,希望原作者可以留言给我,谢谢。

 

代码

#include<cstdio>

const int N=50;

int n,tr[N][N];
long long dp[N][N];

void dfs(int l,int r)
{
    if(l>r)return;
    printf("%d ",tr[l][r]);
    dfs(l,tr[l][r]-1);
    dfs(tr[l][r]+1,r);
    return;

}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%lld",&dp[i][i]),tr[i][i]=i;
    for(int l=2;l<=n;++l)
    {
        for(int i=1;i<=n-l+1;++i)
        {
            int j=i+l-1;
            if(j>n)break;
            for(int k=i;k<=j;++k)
            {
                if(k==i&&dp[i][j]<dp[k+1][j]+dp[k][k])dp[i][j]=dp[k+1][j]+dp[k][k],tr[i][j]=k;
                else if(k==j&&dp[i][j]<dp[i][k-1]+dp[k][k])dp[i][j]=dp[i][k-1]+dp[k][k],tr[i][j]=k;
                else if(k!=i&&k!=j&&dp[i][j]<dp[i][k-1]*dp[k+1][j]+dp[k][k])
                {
                    dp[i][j]=dp[i][k-1]*dp[k+1][j]+dp[k][k];
                    tr[i][j]=k;
                }
            }
        }
    }
    printf("%lld\n",dp[1][n]);
    dfs(1,n);
    return 0;
}

 

posted @ 2017-10-27 20:41  魔方K  阅读(126)  评论(0编辑  收藏  举报