树的重心

93 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情

今天可谓啥也没干,但是看了下acwing基础课一些比较薄弱的东西,qaq。 题目:树和图的遍历深度优先遍历和,树和图的宽度优先遍历。 给定一颗树,树中包含 nn 个结点(编号 1∼n1∼n)和 n−1n−1 条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式

第一行包含整数 nn,表示树的结点数。

接下来 n−1n−1 行,每行包含两个整数 aa 和 bb,表示点 aa 和点 bb 之间存在一条边。

输出格式

输出一个整数 mm,表示将重心删除后,剩余各个连通块中点数的最大值。

数据范围

1≤n≤1051≤n≤105

输入样例

9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

输出样例:

4
难度:简单
时/空限制:1s / 64MB
总通过数:47755
总尝试数:82833
来源:模板题
算法标签

挑战模式

分析

这题一点不简单(确信),我感觉我对树和图很害怕! 首先我们先复习一下树的深度优先遍历

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
int idx=0,e[N],ne[N],h[N];
inline void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
inline void dfs(int u){//深度优先遍历
	st[u]=1;
	for(int i=h[u];i!=-1;i=ne[i]){
		int j=e[i];
		if(!st[j]){
			dfs(j);
		}
	}
}
int main(){
	memset(h,-1,sizeof h);//最重要的一句话,不然MLE/TLE
	int n;
	cin>>n;
	for(int i=1;i<=n-1;i++){
		int a,b;
		add(a,b);
		add(b,a);//无向图,很重要
	}
}

好了,这题其实就是把深度优先遍历改一改,所谓树的重心,就是删去这个某个节点,剩下的联通块点数最大值最小的这个点。

代码

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=200010;
int idx=0,e[N],ne[N],h[N];
bool st[N];
int n;
int ans=N;//ans记录答案
inline void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
inline int dfs(int u){
	st[u]=1;
	int sum=1;//sum记录的是以u为节点的子树的节点个数
	int res=0;//res记录的是每个节点删去之后最大的联通块节点个数;
	for(int i=h[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);//和出去u为节点的子树以为的那个连通块节点个数比较大小。
	ans=min(res,ans);
	return sum;
}
int main(){
	memset(h,-1,sizeof h);//最重要的一句话,不然MLE/TLE
	cin>>n;
	for(int i=1;i<=n-1;i++){
		int a,b;
		cin>>a>>b;
		add(a,b);
		add(b,a);//无向图,很重要
	}
	dfs(1);
	cout<<ans<<endl;
}