题解 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];     

 

这是记忆化搜索能快速返回已经查询到的结果的关键

posted @ 2019-08-29 09:30  剑尘纷飞  阅读(153)  评论(0编辑  收藏  举报