加分二叉树
以下内容来自洛谷:http://www.luogu.org/problem/show?pid=1040
设一个n个节点的二叉树tree的中序遍历为(1,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个用空格隔开的整数,为该树的前序遍历。
样例输入:
5
5 7 1 2 10
样例输出:
145
3 1 2 4 5
以上内容来自洛谷:http://www.luogu.org/problem/show?pid=1040
=================================================================================
状态转移方程:
f(k,k)=value[k]【1<=k<=n】
f(i,j)=max{f(i,i)+f(i+1,j) , f(j,j)+f(i,j-1) , f(k,k)+f(i,k-1)*f(k+1,j) } 【i<k<j】
f(i,j)表示以i为中序遍历的最左边,j为中序遍历的最右边所组成的一棵树的最大积分。
第一行不用解释了,就是如果这棵树只包含根节点(也就是这是叶子),那么他的积分等于自己的价值。
第二行的max部分包括3大部分:
第一个部分,是以最左端为根节点,剩下的部分都是右子树的情况。那么他的分数就是根节点的分数+右子树的分数(因为左子树为空)
那么f(i,i)就是根节点的分数,f(i+1,j)就是右子树的分数
第二个部分,是以最右端为根节点,剩下的部分都是左子树的情况。那么他的分数就是根节点的分数+左子树的分数(因为右子树为空)
那么f(j,j)就是根节点的分数,f(i,j-1)就是右子树的分数
第三个部分是以节点k为根节点的情况。节点k的取值范围是i和j之间,但是不包括i和j(因为这两种情况前面算过了)。
那么分数就是根节点的分数+左子树的分数*右子树的分数。f(k,k)就是根节点的分数,f(i,k-1)是左子树的分数,f(k+1,j)是右子树的分数。
那么有人问了,为甚么要把前两种情况分开写,而不都算作第三种情况呢?我说:如果这么算的话,f(i,j)应该初始化为1【i>j】,你想这么写就自己打循环初始化dp数组吧
打印前序遍历:
还有一个问题,如何打印一棵树的前序遍历呢?我们想一想,打印一棵树的某种遍历,都要找到一棵树的根节点,左子树,右子树。由于题目输入的是中序遍历,给出根节点的位置和左右边界就可以打印这棵树的前序遍历了。我们用一个root[i][j]的二维数组存储来存储中序遍历从i到j的一棵树的根节点的位置。这个数组的元素和dp数组一起更新。至于打印这棵树树,只是一个递归的事情了。
代码:
在dp的时候,注意要先按照中序遍历的长度搜,然后按照头结点搜,这样才是正确的(自己可以想下为什么)
下面是Cpp代码,注释懒得写(注意dp数组就是f数组,dfs函数其实就是一个dp,这些名字我用惯了也懒得改了)
#include <iostream> #include <cstring> using namespace std; int root[32][32],dp[32][32],n,a[32]; int getmax(int i,int j)//计算dp[i][j] and root[i][j] { int ans=max(dp[i][i]+dp[i+1][j],dp[j][j]+dp[i][j-1]); root[i][j]=(ans==dp[i][i]+dp[i+1][j])?i:j; for(int x=i+1;x<=j-1;x++) if(ans<dp[x][x]+dp[i][x-1]*dp[x+1][j]) { ans=dp[x][x]+dp[i][x-1]*dp[x+1][j]; root[i][j]=x; } return ans; } int printTree(int l,int r)//打印 中序遍历左端为l 右端为r的树 { if(l<=r) { cout << root[l][r] << ' '; printTree(l,root[l][r]-1); printTree(root[l][r]+1,r); } } void dfs()//动态规划过程,dfs个毛啊 { memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { dp[i][i]=a[i]; root[i][i]=i; } for(int d=1;d<n;d++)//d是长度 for(int i=1;i<=n-d;i++) dp[i][i+d]=getmax(i,i+d); } int main() { cin >> n; for(int i=1;i<=n;i++) cin >> a[i]; dfs(); cout << dp[1][n] << endl; printTree(1,n); cout << endl; return 0; }