题解 P1040 【加分二叉树】
思路:
以中序遍历的方式输入结点上的值,由于我们需要以前序遍历的方式输出结点,
所以我们必须要知道根结点
我们要确定最大根结点就必须求出最大权值树,我们枚举所有结点作为根结点
如果根结点的编号为x,那么左子树的结点有1~x-1,右子树 结点有x+1~n
代码:
#include<bits/stdc++.h> using namespace std; const int N=35; int tree[N]; //用于存储结点的值 int value[N][N]; //用于存储最大权值和 int root[N][N]; //存储根结点 int dfs(int l,int r) { if(l>r) //空子树,规定其加分为1 return 1; if(l==r) //叶子结点,直接返回该结点的值,且将其记录为根 { root[l][r]=l; return tree[l]; } /* 在每一次搜索的开始,如果已经做过,直接调用答案,回溯 */ if(value[l][r]) //记忆化搜索 防止重复计算 return value[l][r]; // int ans=0,flag; for(int i=l;i<=r;i++) //枚举子层次根结点 { int sum=dfs(l,i-1)*dfs(i+1,r)+tree[i]; if(sum>value[l][r]) { value[l][r]=sum; //最大权值和 root[l][r]=i; //保存根结点,最优的根结点 } } // root[l][r]=flag; //记忆根结点 return value[l][r]; //记忆化 } //如果根结点的编号为x,那么左子树的结点有1~x-1,右子树 结点有x+1~n //先序输出函数 void show(int l,int r) { if(l>r) return ; cout<<root[l][r]<<' '; show(l,root[l][r]-1); show(root[l][r]+1,r); } int main() { std::ios::sync_with_stdio(false); int n; cin>>n;//输入节点个数 for(int i=1;i<=n;i++) cin>>tree[i];//输入每个节点的分数 cout<<dfs(1,n)<<endl; //进入记忆化搜索部分 show(1,n);//输出函数部分 return 0;//程序结束 }
核心代码:
for(int i=l;i<=r;i++) //枚举子层次根结点 { int sum=dfs(l,i-1)*dfs(i+1,r)+tree[i]; if(sum>value[l][r]) { value[l][r]=sum; //最大权值和 root[l][r]=i; //保存根结点,最优的根结点 } }
特别注意:
if(value[l][r]) //记忆化搜索 防止重复计算 return value[l][r];
这是记忆化搜索能快速返回已经查询到的结果的关键