先看题目:
给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 n,表示树的结点数。
接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。
题目比较阴间,我们先翻译一下
解答代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
const int M = 2*N;
int n;
int h[N],e[M],ne[M],idx;
bool st[N];//用来判断编号为 i 的结点是否被搜过
int ans = N;//这个ans要足够大,不然后面的min(ans,res)无法更新
void add(int a,int b)//这里的a和b指的是结点的值,本题环境中可以理解为是结点的编号
//在值为 a 结点的后面,插入结点 b
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
}
int dfs(int u)//这里的dfs()返回的是编号u结点的子树的所有节点数
//注意:一定是子树,而不包括父节点
{
int res = 0;//res存储的是编号u结点的所有子树中节点数的最大值
//换人话说,就是编号u结点所有子树中,结点数最多的那个子树的节点数
int sum =1;//指的是包含编号u结点,编号u结点的子树的所有的节点数和
st[u] = true;
for (int i = h[u];i!= -1;i =ne[i])
{
int j = e[i];
if (!st[j])
{
int s =dfs(j);//这里的dfs(j)代表编号j结点的所有子树的所有节点数
sum += s;//sum表达的是编号u结点的所有子树的所有节点数
//而编号j结点是编号u结点的子节点,或者可以理解为是编号u结点的一棵子树
//的开头。
//那么就可以得到下列公式:
//dfs(u) = dfs(j) + dfs(k) +...+ dfs(l) +1
//公式中的j,k,...,l 就是编号u结点的所有子结点(一定是子结点,而不是父节点)
//因为编号u一定是从父节点走过来的,那么父节点一定已经被标记了被搜索过
//所以不可能再往回走
//而公式中的1就是编号u结点本身。
//可以类比斐波那契额数列的公式:f(n) = f(n-1)+f(n-2),是差不多的道理
res = max(res,s);//得到编号u结点所有子树中结点数最多的子树的结点数
}
}
res = max(res,n-sum);//这个代码可以放在图中讲
ans = min(ans,res);//这一句代码是为了题目服务,找到最小的最大值
return sum;//最后的返回值是sum,而不是ans
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
memset(h,-1,sizeof(h));
for (int i = 0;i<n-1;++i)
{
int a,b;
cin >> a >> b;
add(a,b);
add(b,a);
}
dfs(1);//这里随便取一个小于n的数就行
//之所以随便取一个数就行,是因为最后要的是将重心删除后,
//剩余各个连通块中点数的最大值的最小值。
//而不是特定结点的所有子树的结点数之和
//而无论这个参数取多少,dfs一定能走遍所有的结点,比较所有的删除特定点
//后的剩余各个连通块中点数的最大值
cout << ans ;
return 0;
}
1.int dfs(int u){}函数
具体的 深度优先遍历 过程在 树与图()那一篇blog中有详解,不懂的可以去看
这一篇我们主要看一看如何运用在实际题目中运用 深度优先遍历
(1)
首先 dfs(int u) 的含义在本题的环境中是返回编号u结点的所有子树的所有结点数之和(注意:一定是子树)
(2)
int res = 0;//res代表的是编号u结点的所有子树中结点数最多的子树的结点数
int sum = 1;//sum代表的是编号u结点的所有子树的所有结点数之和
//这里之所以初始值是1,因为将编号u结点也包含进去了
(3)
根据题意,我们可以推断出一个递归式:
dfs(u) = dfs(j) + dfs(k) +...+ dfs(l) +1
其中: j,k,l... 是编号u结点的所有子节点,1代表编号u结点本身也算结点之一。
(4)
res= max(res,s);
这段代码将编号 u 结点的所有子树中拥有结点数最多的子树的结点数记录下来。
int s = dfs(j);
s就是所要进入的子树的其中所有的结点数。
(5)
res = max(res,n-sum);