18929 最优二叉树

划重点:二叉树必用二分递归的算法。

此题目初学者理解比较困难,需要有dfs算法基础,且熟悉二叉树算法。

解题思路:拿到问题之后,先用暴力枚举的思路去思考解法。首先任何一个点都可能是树根,

假设我们选择第i个点作为树根,那么树结构就是左子树[1,i-1] ,树根i,右子树 [i+1,n]。

在[1,i-1]中要选择一个点作为左子树树根(假设是j),在[i+1,n]中选一个点作为右子树的树根(假设是k),

那么结果就是:左子树(根为j时)的最优值+a[j]*a[i]+右子树(根为k)最优值+a[k]*a[i]

分析到这里能得到,一棵树的最优值不但和其自身有关,还和这棵树的父节点有关。比如左子树根j的选择,不能只考虑“左子树自身的最优值”,必须综合考虑树根j和树根父节点i的乘积。

因此设计递归函数时,需要三个参数dfs(int l,int r,int root),[l,r]中序序列区间,root是[l,r]对应二叉树的父节点,在[l,r]之间选择树根j时,最终计算结果必须得考虑j和root的乘积。

我们把整个序列[1,n]的父节点定义为0号结点,其a[0]=0。(这是树结构中一种常用的边界处理技术,给树根一个不存在或值为0的父节点,便于计算)。

为了避免重复计算,此处使用了记忆化搜索。(其实本问题和区间dp思想是类似的,不过一提到二叉树自然而然就会想到递归下降的方法)

#include <bits/stdc++.h>//记忆化dfs
typedef long long ll;
using namespace std;
ll n,a[305],dp[305][305][305]; /**<dp[l][r][root]表示l...r是一颗子树,树根不确定,但这颗子树的父节点是root  */
ll dfs(int l,int r,int root)   /**< 其实root不是l-1,就是r+1,思考中序序列中树根的位置 */
{
    if(l>r)
        return 0;
    if(dp[l][r][root]<=3e9)
        return dp[l][r][root];
    ll i,j,k,ans=3e9;
    for(i=l; i<=r; i++) /**< i为[l,r]根时,左右子树进行划分 */
    {
        int temp1=dfs(l,i-1,i);
        int temp2=dfs(i+1,r,i);
        ans=min(ans,temp1+temp2+a[i]*a[root]);/**< 选i为根是否合适还得考虑上a[i]*a[root] */
    }
    return dp[l][r][root]=ans;
}
int main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int i,j,k,l,ans=INT_MAX;
    memset(dp,127/3,sizeof(dp));/**< dp数组初始化 */
    cin>>n;
    for(i=1; i<=n; i++)
        cin>>a[i];
    dfs(1,n,0);/**< 1到n的树的最优值,0是不存在的树根 */
    cout<<dp[1][n][0];
    return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值