前言
date:2019-11-15 CSP1 前一天
作为一个去年还是个稚嫩的选手参加NOIP2018的OIer,感到自己仿佛经历了一次时代大变革,心中感慨万分。
那是无知、迷茫的心情在考场上作答。知道今天打开这个题目,我才发现曾经的自己是多么氺,连这道题都没有拿全分。
(后来回忆起当时连搜索都勉勉强强,也算是情理之中吧)
题目
一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:
- 二叉树;
- 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。
下图中节点内的数字为权值,节点外的 id 表示节点编号。
现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。
注意:只有树根的树也是对称二叉树。本题中约定,以节点 T 为子树根的一棵“子 树”指的是:节点T 和它的全部后代节点构成的二叉树。
输入格式
第一行一个正整数 n,表示给定的树的节点的数目,规定节点编号 1∼n,其中节点 11 是树根。
第二行 n 个正整数,用一个空格分隔,第 i 个正整数 vi 代表节点i 的权值。
接下来 n 行,每行两个正整数 li, ri,分别表示节点 i 的左右孩子的编号。如果不存在左 / 右孩子,则以 −1 表示。两个数之间用一个空格隔开。
输出格式
输出文件共一行,包含一个整数,表示给定的树的最大对称二叉子树的节点数。
分析
1. 怎样去构建一棵二叉树:
(这还用说?)递归DFS
1每一个节点设参数l,r表示它的左右孩子节点的编号;
2每一个节点设参数size表示它的子节点个数(包括自己);
3每一个节点设参数val表示它的权值
其中,1、3可以在输入中完成,2可以通过递归完成(递归处理之后统计结果)
2. 怎样判断一棵子树是不是“对称二叉树”:
1若两个儿子节点均不存在,则当前是对称二叉树;
2若有且只有一个儿子节点存在,则当前不是对称二叉树;
3若两个儿子节点存在但权值不一致,则当前不是对称二叉树;
4若两个儿子节点存在且权值一致,则进一步检查左儿子的左儿子和右儿子的右儿子是否对称,左儿子的右儿子和右儿子的左儿子是否对称,若都对称则当前是对称二 叉树,否则就不是。(递归下去)
3.怎样处理答案:
当这棵子树是“对称二叉树”的时候,我们更新答案为其与当前节点的size的最大值(顺势而为)
细节
1.一定要把每一个节点的size赋值为1
2.一定注意叶子节点是“对称二叉树”
3.一定不要用scanf(至于原因我也不知道,只是因为我提交的时候用scanf又是Wrong Answer 又是 Memory Limited Errow,改成cin之后就AC了)
4.一定要把答案的初始值赋为1(一棵二叉树总会有叶子节点)
代码
#include<bits/stdc++.h> #pragma GCC optimize(2) using namespace std; int n,res=1;//赋值 struct node//节点结构体 { int l,r,size,val; }a[1000005]; inline bool judge(int lef,int rig)//判断一当前节点为根节点的子树是否为“对称二叉树” { if(lef==-1 and rig==-1)//叶子节点 return 1; if(lef==-1 or rig==-1)//独生子女 return 0; if(a[lef].val^a[rig].val)//左右孩子权值不相等 return 0; return judge(a[lef].l,a[rig].r)&judge(a[lef].r,a[rig].l);//递归检查 } inline void dfs(int k) { if(a[k].l^-1)//向左建树 { dfs(a[k].l); a[k].size+=a[a[k].l].size; } if(a[k].r^-1)//向右建树 { dfs(a[k].r); a[k].size+=a[a[k].r].size; } if(judge(a[k].l,a[k].r)) res=max(res,a[k].size);//更新答案 } int main() { ios::sync_with_stdio(false); cin>>n; for(register int i=1;i<=n;i++) cin>>a[i].val,a[i].size=1; for(register int i=1;i<=n;i++) cin>>a[i].l>>a[i].r; dfs(1);//建树兼更新答案 cout<<res<<endl; return 0; }
总结
考前最后一天,祝所有OIer RP++,AK全场!