NOI 1192 加分二叉树 解题报告

NOI 1192 加分二叉树 解题报告

题目链接: http://judge.noi.cn/problem?id=1192

 

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

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

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

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

1tree的最高加分

2tree的前序遍历

 

算法:此题为练习树形dp的好题.题目直接给出了最优子结构: subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数,而中序遍历的定义{左子树、根、右子树},r是中序遍历的根,所以其r的左子树为{i,i+1,···,r-1},右子树为{r+1,···j},那么我们就可以据此得出状态转移方程:

i=j   f[i][j]=顶点i的分数

i>j    1

i<j    max{f[i][r-1]*f[r+1][j]+f[r][r]

 

AC代码:

 

#include <stdio.h>

#include <string.h>

#define N 35

 

int n, way[N][N];

__int64 f[N][N], tree[N];

bool firsterite;

 

__int64 search(int l, int r)

{

       int i;

       __int64 now;

 

       if (l > r)

       {

              return 1;

       }

       else

       {

              if (f[l][r] == -1)

              {

                     for (i = l; i <= r; i++)

                     {

                            now = search(l, i - 1) * search(i + 1, r) + f[i][i];

                            if (now > f[l][r])

                            {

                                   f[l][r] = now;

                                   way[l][r] = i;

                            }

                     }

              }

              return f[l][r];

       }

}

 

void write(int l, int r)

{

       if (l > r)

       {

              return;

       }

       if (firsterite)

       {

              firsterite = false;

       }

       else

       {

              printf(" ");

       }

       printf("%d", way[l][r]);

       write(l, way[l][r] - 1);

       write(way[l][r] + 1, r);

}

 

int main()

{

       int i;

       scanf("%I64d", &n);    

       memset(f, -1, sizeof(f));

       for (i = 1; i <= n; i++)

       {

              scanf("%I64d", &tree[i]);

              f[i][i] = tree[i];

              way[i][i] = i;

       }

       __int64 res = search(1, n);

       printf("%I64d/n", res);

       firsterite = true;

       write(1, n);

       printf("/n");

 

       return 0;

}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值