Code[VS] 1090 加分二叉树
2015-02-21 12:24
302 查看
【题意】
已知:一颗树tree,n,d
条件:①有结点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]
,且得到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
,rt
,s
;
long long f
;
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
);
}
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] 数据类型大的变量可以加小的变量,小的不能加大的
已知:一颗树tree,n,d
条件:①有结点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]
,且得到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
,rt
,s
;
long long f
;
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
);
}
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] 数据类型大的变量可以加小的变量,小的不能加大的
相关文章推荐
- CODEVS 1090 加分二叉树
- [CODEVS1090][NOIP2013]加分二叉树(树形dp)
- 【基础练习】【区间DP】codevs1090 加分二叉树题解
- CJOJ 1010【NOIP2003】加分二叉树 / Luogu 1040 加分二叉树 /CodeVS 1090 加分二叉树
- [codevs1090]加分二叉树
- codevs 1090 加分二叉树
- 【基础练习】【区间DP】codevs1090 加分二叉树题解
- Cpp环境【NOIP2003 P3】【Vijos1100】【Code[VS]1090】【CQYZOS2816】加分二叉树
- 加分二叉树[NOIP 2003提高组][Codevs 1090]
- Codevs1090 加分二叉树
- codevs 1090 加分二叉树
- codevs 3083 二叉树
- codevs1063 luogu1090 合并果子
- 二叉树计数(codevs 3112)
- codevs 3143 二叉树
- codevs 1501二叉树最大宽度和高度
- CODE[VS]1501 二叉树最大宽度和高度
- 多叉树转二叉树+树形dp(codevs 1746 贪吃的九头龙 2002noi)
- 【NoI 2002】【CodeVs 1746】【贪吃的九头龙】【树形dp】【记忆化搜索】【多叉树转化二叉树】
- CodeVS3112 二叉树计数