AcWing 846. 树的重心 超详细解析

在这里插入图片描述
https://www.acwing.com/problem/content/848/

首先要看清题目是 n个点 (n-1) 条边。

重心的定义是啥不多说,不懂的多看几次y总的视频,这不难理解。

题目给的是无向图,我们可以用有向图建两条边,来表示无向图。
题目给的点的范围是 小于1e5。

const int N=1e5+10;
const int M=N*2;
int h[N],e[M],ne[M],idx;
bool st[N];
int ans=N;

所以上面不难理解,因为是两条边故 e[ M ] , ne[ M ] ,头节点就是n个 h [ N ]

st[N]  是用来方式重复遍历的。 因为我们只需枚举每一个点一次就行了。
所以用其标记来保证每一个点只枚举一次

下面开始分析: 如何求去掉一个点后各个连通块点的个数。
在这里插入图片描述
你会发现: 取掉一个点后,此时有三个连通块(用蓝色标记)。

其实本质是两块内容

在这里插入图片描述

由上图可以看到: 本质分成了两个部分。

  • 第一部分: 以4为头节点的其每一个儿子的点的个数(我们取最大的)
  • 第二部分: 4作为一个子节点求其所对应的连通块的点数.

那么4作为子节点的点数= n(总的点数) - 4的儿子的节点数之和。

到这里我想各位应该已经懂了一大半了。
下面看代码分析:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10;
const int M=N*2;
int n;
int h[N],e[M],ne[M],idx;
bool st[N];
int ans=N;
void add(int a,int b)//向以a头节点 加入b
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u)//求以 u为头节点的子节点的点数之和
{
	st[u]=true;
	int sum=1,res=0;  //sum=1  是因为加上去掉的那个数
	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; //求以j为头节点的子节点的点数之和
		} 
	}
	
	res=max(res,n-sum);//求头节点所在连通块  和 其子节点最大的点数 中最大的值
	ans=min(ans,res);//求我们每一种情况中,最小的值
	
	return sum;
}
int main(void)
{
	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(4);
	cout<<ans<<endl;
	return 0;
}

首先 因为我这里举的例子是从4开始的,故写的dfs(4) 代表从 4开始枚举。
其实 dfs() 从1到n中的任何一个数枚举都是一样的,因为我们都是只枚举一遍。
不能从0开始是因为我们的节点是从1开始的。你不能枚举一个没有的点。

刚开始:
在这里插入图片描述
刚开始 4可以到的有 1 3 6
然后1 没有走过,就求去掉1后的最大点数 , 1又可以到达 2 7 以此递归。
3
6
在这里插入图片描述
我想 st[ N ] 的作用大家都知道了吧。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
int h[N],e[N],ne[N],idx,n,ans=N;
bool vis[N];
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dfs(int u)
{
    vis[u]=1;
    int res=0;
    int sum=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!vis[j])
        {
            int t=dfs(j);
            res=max(res,t);
            sum+=t;
        }
    }
    res=max(res,n-sum);
    ans=min(ans,res);
    return sum;
}
int main(void)
{
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n-1;i++)
    {
        int a,b; cin>>a>>b;
        add(a,b),add(b,a);
    }
    dfs(1);
    cout<<ans;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值