加分二叉树
描述
设一个n个节点的二叉树tree的中序遍历为(l,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个用空格隔开的整数,为该树的前序遍历。
限制
每个测试点1s
来源
NOIP2003第三题
都说是DP。。。我开始想的是暴力。。。然后瞎写了写怎么就写了个DP。。。。
我的想法是因为n最多只有30个。。那么我们可以枚举根节点,然后分别在根的两边进行同样的操作。。。然后就变成了枚举区间枚举节点。。。
不过枚举区间的时候要记住从后往前。。。
4 #include<iostream> 5 #include<cstdlib> 6 #include<cmath> 7 #include<cstring> 8 #include<cstdio> 9 #include<algorithm> 10 #include<string> 11 #include<map> 12 #include<queue> 13 #include<vector> 14 #include<set> 15 #define inf 1000000000 16 #define maxn 100+5 17 #define maxm 100+5 18 #define eps 1e-10 19 #define ll long long 20 #define for0(i,n) for(int i=0;i<=(n);i++) 21 #define for1(i,n) for(int i=1;i<=(n);i++) 22 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 23 #define for3(i,x,y) for(int i=(x);i>(y);i--) 24 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go) 25 using namespace std; 26 int read() 27 { 28 int x=0,f=1;char ch=getchar(); 29 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 30 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 31 return x*f; 32 } 33 int f[maxn][maxn],a[maxn],root[maxn][maxn]; 34 void output(int l,int r){ 35 if(l>r)return; 36 printf("%d ",root[l][r]); 37 output(l,root[l][r]-1); 38 output(root[l][r]+1,r); 39 } 40 int main(){ 41 freopen("input.txt","r",stdin); 42 freopen("output.txt","w",stdout); 43 int n=read(); 44 f[0][0]=1; 45 for1(i,n) 46 root[i][i]=i; 47 for1(i,n)f[i][i]=a[i]=read(),f[i][i-1]=1; 48 for3(i,n,0) 49 for2(j,i+1,n){ 50 for2(k,i,j){ 51 if(f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k]){ 52 root[i][j]=k; 53 f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k]; 54 } 55 } 56 } 57 printf("%d\n",f[1][n]); 58 output(1,n); 59 return 0; 60 }