今天做了个题
https://www.luogu.com.cn/problem/solution/P6591
无向无环连通图的子树遍历操作
首先这个题分两种办法
1.暴力平方复杂度级别
这个具体代码就不展示了
就是一一枚举每个节点作为father节点时的直连子节点的子节点数量,如果都相同那么就算上这个father
2.深搜
首先是存储
老生长谈罢了,利用vector来存储,原理是孩子链表存储法
for(int i=1;i<=n-1;i++)
{
cin>>a>>b;
es[a].push_back(b);
es[b].push_back(a);
}
接下来是深搜
我们利用注释来对深搜进行分析
先看纯纯的深搜
int dfs(int x,int father) x:根节点 ;father:x的根节点 意义为搜索以x为根节点的子节点数
{
int size1 = es[x].size();
for(int i=0;i<=size1-1;i++)
if(es[x][i] != father)
{
d[x] += dfs(es[x][i],x);d[x]代表x的子树大小 注意!是子树大小不是子节点数!
}
++d[x];为了避免重复搜索,虽然父节点在dfs的时候没算上,但父节点也是所谓子节点的一个,所以也要算上。
return d[x];递归返回
}
再来看加上了判断的深搜
int dfs(int x,int father)
{
int size1 = es[x].size(),num = 0;num用来暂存子树大小,嗯对,或许写成temp更好理解,或者temp_num
root[x] = 1;在一开始我们判定每个节点都可以成为根 毕竟一开始你还没加子树,子树为0成为根也说得过去
for(int i=0;i<=size1-1;i++) 搞他所有子节点得到子树大小
if(es[x][i] != father) 防止形成闭环
{
d[x] += dfs(es[x][i],x);
if(!num) 此时num为0代表还没有存到子树的大小,那么就让我们给他第一个值,也可以说是基准值
num = d[es[x][i]]; //d[x]是x子树的大小,d[es[x][i]] 就是x的一个子节点的子树的大小
if(num != d[es[x][i]])如果后来的值和基准值不相等就可以寄了
root[x] = 0; 寄了
}
++d[x];
if(x != 1 && num && num != n - d[x]) 根节点和叶子节点跳过
root[x] = 0;
return d[x];
}
至于这个
num != n - d[x]
额,x的一个子节点的子树的大小!=总节点数-x子树的大小,,啊这,自己琢磨琢磨
完整代
#include <bits/stdc++.h>
using namespace std;
vector <int> es[1000000+100];
bool root[1000000+100];
int d[1000000+100],n;
int dfs(int x,int father)
{
int size1 = es[x].size(),num = 0;
root[x] = 1;
for(int i=0;i<=size1-1;i++)
if(es[x][i] != father)
{
d[x] += dfs(es[x][i],x);
if(!num)
num = d[es[x][i]];
if(num != d[es[x][i]])
root[x] = 0;
}
++d[x];//为了避免重复搜索,虽然父节点在dfs的时候没算上,但父节点也是子节点的一个,所以也要算上。
if(x != 1 && num && num != n - d[x])
root[x] = 0;
return d[x];
}
int main()
{
cin>>n;
int a,b;
for(int i=1;i<=n-1;i++)
{
cin>>a>>b;
es[a].push_back(b);
es[b].push_back(a);
}
dfs(1,0);
for(int i=1;i<=n;i++)
if(root[i])
cout<<i<<" ";
cout<<endl;
return 0;
}