NOI 1192 加分二叉树 解题报告
题目链接: http://judge.noi.cn/problem?id=1192
题意: 设一个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的前序遍历
算法:此题为练习树形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;
}