这里在讲一下重心的概念,就是每删掉一个结点,剩下的连同块中都取最大值,然后每个最大值比较,最小的那个就是重心
本题思路:
在dfs的时候统计子树的大小
假如这里我们要统计删掉4结点的连通块,这里有三部分(3,9)、(6)、(1,2,7,8,5)
3,6是4的子树,而9是三的子树,在递归的时候我们就可以统计出来,而上面一部分我们将他称为4的父连通块,我们可以用总数减去以4为根的子树大小(包括4)。就可以求出父连通块的数量
这里要注意,其实从哪个点都可以开始枚举,因为这是一个无向图,都可以走通,但每个点最多走一次,所以要防止子结点又走回父节点
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
const int M =2*N;
int n;
int e[M],head[N],ne[M],idx;
int ans=N;
bool st[M];//状态数组,防止子节点搜索父节点
void add(int a,int b)//构造邻接表(无向图)
{
e[idx]=b,ne[idx]=head[a],head[a]=idx++;
}//头插法
//返回以u为根的子树大小
int dfs(int u)
{
st[u]=true;//st标记当前点被搜过
int res=0;//res为最大连通块的数量(删除某个结点后)
int sum=1;//当前子树的大小算上根所以从1开始
for (int i=head[u];i!=-1;i=ne[i])
{
int j=e[i];
if (!st[j])
{
int s=dfs(j);
res=max(res,s);
sum+=s;
}
}
res=max(res,n-sum);//删除某点后,判断子连通块和父连通块谁大
ans=min(ans,res);//最小的最大值
return sum;
}
int main()
{
cin>>n;
memset(head, -1, sizeof head);
for (int i=0;i<n-1;i++)
{
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
dfs(1);
cout<<ans<<endl;
return 0;
}
# acwing 846. 树的重心
N = 100010
# 将重心删除后,剩余各个连通块中点数的最大值,先初始化为最大值
ans = N
def add(a,b):
global idx
e[idx] = b
ne[idx] = h[a]
h[a] = idx
idx += 1
# 深度优先遍历一般都有个for循环,
# 然后再在for循环中深度遍历
def dfs(u):
global ans
# 标记访问过树节点u
st[u] = True
# sum:以u为根的树的节点数(包括u)
# res:删除树节点u后,最大连通子图(子树)节点数
sum, res = 1, 0
# 找到存储与u节点相连的树节点的存储节点
i = h[u]
while i!=-1:
# 树节点j为与树节点u直接相连的树节点
j = e[i]
# 如果树节点j没有被访问过
if not st[j]:
# 以树节点u的子节点j为根的树的大小
s = dfs(j)
# 求最大连通子图(子树)的大小
res = max(res,s)
# 先把这棵子树的节点数加上
sum += s
# 继续访问树节点u的下一棵子树
i=ne[i]
# 现在把以u为根的所有子树的连通数情况求出来了,但是还要将
# 当前结果和整棵树除掉u这棵子树的数量进行对比
res = max(res,n-sum)
# 比较删除当前树节点的最大连通子图节点数和
# 删除其它节点得出的最大连通子图节点数
ans = min(ans,res)
return sum
if __name__ == '__main__':
h, e, ne, idx = [-1]*N, [0]*2*N, [0]*2*N, 0
# 记录某个树节点是否被访问过
st = [False]*N
n = int(input())
# 无向图,所以要从a->b,b->a都要插入一条边
for i in range(n-1):
a,b = map(int, input().split())
add(a,b)
add(b,a)
dfs(1)
print(ans)