Code[VS] 1090 加分二叉树

来源:互联网 发布:我的淘宝如何换帐号 编辑:程序博客网 时间:2024/06/09 04:52

【题意】

已知:一颗树tree,n,d[N]

条件:①有结点1..n,每个点有分数d[i],tree满足中序遍历为1..n

      ②子树subtree的加分计算方法为:左子树加分×右子树加分+根的分数

      ③空子树加分为1,叶子的加分就是叶节点本身的分数,不考虑它的空子树。

求:一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree,输出 ①tree的最高加分 ②tree的前序遍历

【构思】

树有无后效性-->树形DP;

由于满足中序遍历,所以一颗子树的结点编号必然在一连续区间内;

设f[l][r]表示区间[l,r]的最大加分,rt[l][r]为最大加分时rt是什么;

边界:f[l][r]=1(l>r),f[l][r]=d[l](l=r)

方程:f[l][r]=max(f[l][i-1]+f[i+1][r]+d[i])

结果即f[1][n],且得到rt后直接可以建树,2问都解决了;

时间复杂度:O(n^3)

空间复杂度:O(n^2)

【实现】

#include <cstdio>

#include <cstring>

#include <cstdlib>


#define MIN -5000000000


using namespace std;


const int N=31;


int n,d[N],rt[N][N],s[N];

long long f[N][N];


void init(void)

{

scanf("%d",&n);

for (int i=1;i<=n;i++) scanf("%d",&d[i]);

}


long long DFS(int l,int r)

{

if (l>r) return 1;

if (f[l][r]^MIN) return f[l][r];

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

{

long long t=DFS(l,i-1)*DFS(i+1,r)+d[i];

if (f[l][r]<t)

{

f[l][r]=t;

rt[l][r]=i;

}

}

return f[l][r];

}


void Get(int l,int r)

{

if (l>r) return;

if (l==r) {s[++s[0]]=l;return;}

s[++s[0]]=rt[l][r];

Get(l,rt[l][r]-1);

Get(rt[l][r]+1,r);

}


void work(void)

{

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

for (int j=i;j<=n;j++) f[i][j]=(i==j?d[i]:MIN);

printf("%lld\n",DFS(1,n));

Get(1,n);

for (int i=1;i<n;i++) printf("%d ",s[i]);

printf("%d\n",s[n]);

}


int main(void)

{

init();

work();

return 0;

}

【回顾】

[1] DP无后效性的判定  ①空间的单项变化  ②数值的单调变化  ③树结构

[2] 树的遍历的性质

①单独性质

前序遍历   第1个为根 一段区间在后[i,i+size[i]-1]

中序遍历   当序列满足1..n时,子树也为连续区间 排序

后序遍历   最后为根     一段区间在前[i-size[i]+1,i]

②关联性质

遍历长度相同

[3] 树的遍历:搜索

[4] 数据类型大的变量可以加小的变量,小的不能加大的

0 0
原创粉丝点击