递归

4313. 满二叉树等长路径 - AcWing题库

对于此类树的题目, 递归是一种很常用的方法。

我们从一般性的角度出发,并特殊化题目。

由于题目是个满二叉树,我们可以将整个二叉树看做一个只有三个节点的满二叉树(特殊化处理),即:根节点root,左子树节点Lroot,右子树节点Rroot。

这里的左、右子树节点是把整个左(右)子树抽象为一个节点。

例如:一个满二叉树从左到右从上到下的节点编号:1234567

那么,root=1,左子树节点=(2(4,5)),右子树节点+(3(6,7))。(有递归的味道了!)

接下来我们考虑答案,很显然,要保证所有叶节点到根节点的距离相等,就要使得左子树与右子树到根节点的值等于一个d(d>=max(L, R)),这里的L和R分别代表左右子树节点到根节点的距离,由于我们要保证答案是最小的,所以d取max(L,R),这样只需要修改一个子树的值,而不必修改两个子树的值。

然后由特殊化推广到一般性,由于我们的子树可能也是一个树,而不单单是一个节点,所以说:

  • 这里的L=(该左子树的左右子树节点LLroot,LRroot)到(该左子树的根节点Lroot)的距离 + (该左子树根节点Lroot)到(其根节点root)的距离
  • 同理,R=(该右子树的左右子树节点RLroot,RRroot)到(该右子树的根节点Lroot)的距离 + (该右子树根节点Lroot)到(其根节点root)的距离
  • 注意,上面的标红色的距离,是我们已经修改过得距离!也就是说,LLroot和LRroot到Lroot的距离是相等的!同理,RLroot和RRroot到Rroot的距离也是相等的!(又是递归的味道!)
  • 那么现在,思路是不是就清晰多了!接下来看代码吧。

再次说明一下,上面的答案肯定是满足最小值得要求的,因为我们本质上还是从上层修改。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1 << 12;

int n, m, w[N];
int ans;//修改的次数

int dfs(int u) {
//这里递归的返回值是根节点u的左右子树经过修正后到跟节点的距离,而不是修正的次数
//显然左右子树到根节点的距离是相等的
    if((u << 1) > m) {
        //如果是叶子节点,那么不存在左右子树
        //可以理解为此时左右子树到根(叶子)节点的距离为0
        return 0;
    }
    //否则,修正左右子树到根节点的距离
    int l = dfs(u << 1) + w[u << 1];
    //(该左子树的左右子树节点LLroot,LRroot)到(该左子树的根节点Lroot)的距离 + 
    //(该左子树根节点Lroot)到(其根节点root)的距离
    int r = dfs(u << 1 | 1) + w[u << 1 | 1];
    ans += abs(l - r);  //修正的值
    return max(l, r);   //修正后左右子树到根节点的距离
}

int main()
{
    cin >> n;
    m = (1 << n + 1) - 1;//运用位运算比pow快
    for(int i = 2; i <= m; i ++ )   cin >> w[i];
    
    dfs(1); //从根节点开始往下递归
    
    cout << ans << endl;
    
    return 0;
}

posted @ 2022-05-05 08:41  光風霽月  阅读(8)  评论(0编辑  收藏  举报