• 树的重心


    关于树的重心

    Define

    一棵具有n个节点的无向树,若以某个节点为整棵树的根,他的每个儿子节点的大小都>=n/2 ,则这个节点即为该树的重心

    性质

    1. 删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心
    2. 树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等
    3. 两个树通过一条边合并,新的重心在原树两个重心的路径上
    4. 树删除或添加一个叶子节点,重心最多只移动一条边

    解法

    1.dfs求解

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #define ll long long
    
    const ll maxn=1e5+10;
    ll n,S,tot,ans,sum=maxn*maxn;
    ll head[maxn*2],vis[maxn],dep[maxn],siz[maxn];
    struct node
    {
    	ll u,v,w,nxt;
    } s[maxn*2];
    
    inline void add(ll u,ll v)
    {
    	s[++tot].v=v;
    	s[tot].nxt=head[u];
    	head[u]=tot;
    }
    
    inline void dfs(ll x)
    {
    	vis[x]=1;
    	siz[x]=1;
    	ll maxx=-1;
    	
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll y=s[i].v;
    		
    		if(vis[y]) continue;
    		
    		dfs(y);
    		
    		siz[x]+=siz[y];
    		
    		maxx=std::max(maxx,siz[y]);
    	}
    	
    	maxx=std::max(maxx,n-siz[x]);
    	
    	if(sum>maxx)
    	{
    		sum=maxx;
    		ans=x;
    	}
    }
    
    int main(void)
    {
    	scanf("%lld %lld",&n,&S);
    	
    	for(int i=1;i<=n-1;i++)
    	{
    		ll x,y;
    		scanf("%lld %lld",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	
    	dfs(S);
    	
    	printf("%lld %lld",ans,sum);
    }
    

    2.朴素用 (O(n^2)) 的暴力求解多个重心编号

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #define ll long long
    
    const ll maxn=1e3+10;
    ll n,S,tot;
    ll fa[maxn],head[maxn*2],siz[maxn];
    struct node
    {
    	ll u,v,nxt;
    }s[maxn*2];
    
    inline void add(ll u,ll v)
    {
    	s[++tot].v=v;
    	s[tot].nxt=head[u];
    	head[u]=tot;
    }
    
    inline void dfs(ll x)
    {
    	siz[x]=1;
    	
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll y=s[i].v;
    		
    		if(y==fa[x]) continue;
    		
    		fa[y]=x;
    		
    		dfs(y);
    		
    		siz[x]+=siz[y];
    	}
    }
    
    inline ll judge(int x)
    {
    	if(n-siz[x]>(n/2))//应用性质 1 判断
    	{
    		return 0;
    	}
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll y=s[i].v;
    		
    		if(fa[y]==x&&siz[y]>(n/2)) //同上
    		{
    			return 0;
    		}
    	}
    	return 1;
    }
    
    int main(void)
    {
    	scanf("%lld %lld",&n,&S);
    	for(int i=1;i<=n-1;i++)
    	{
    		ll x,y;
    		scanf("%lld %lld",&x,&y);
    		
    		add(x,y);
    		add(y,x);
    	}
    	
    	dfs(S);
    	
    	for(int i=1;i<=n;i++)
    	{
    		if(judge(i))
    		{
    			printf("%d ",i);
    		}
    	}
    	
    	return 0;
    }
    

    3.求解一棵树的所有子树的重心

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<math.h>
    #include<vector>
    #include<queue>
    #define ll long long
    
    const ll maxn=1e3+10;
    ll n,S,tot;
    ll fa[maxn],head[maxn*2],siz[maxn],zx[maxn],an[maxn];
    struct node
    {
    	ll u,v,nxt;
    }s[maxn*2];
    
    inline void add(ll u,ll v)
    {
    	s[++tot].v=v;
    	s[tot].nxt=head[u];
    	head[u]=tot;
    }
    
    inline void dfs(ll x)//求以 S 为根的树的各个子树的大小
    {
    	siz[x]=1;
    	
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll y=s[i].v;
    		
    		if(y==fa[x]) continue;
    		
    		fa[y]=x;
    		
    		dfs(y);
    		
    		siz[x]+=siz[y];
    	}
    }
    
    inline ll judge(int x,ll y)//判断 x 是否为 y 的重心
    {
    	if(siz[y]-siz[x]>(siz[y]/2))
    	{
    		return 0;
    	}
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll v=s[i].v;
    		
    		if(fa[v]==x&&siz[v]>(siz[y]/2))
    		{
    			return 0;
    		}
    	}
    	return 1;
    }
    
    inline void get(ll x) //求每个子树的重心
    {
    	ll p=-1;
    	for(int i=head[x];i;i=s[i].nxt)
    	{
    		ll y=s[i].v;
    		if(y!=fa[x])
    		{
    			get(y);
    			if(siz[y]>siz[x]/2) p=y;//可能的重心
    		}
    	}
    	if(p==-1) zx[x]=x;
    	else zx[x]=zx[p];
    	
    	while(!judge(zx[x],x)) // 从子树的重心范围内寻找重心
    	{
    		zx[x]=fa[zx[x]];
    	}
    	
    	ll q=zx[x];
    	
    	for(int i=head[q];i;i=s[i].nxt) //应用性质 3 枚举求解另一重心
    	{
    		ll v=s[i].v;
    		
    		if(judge(v,x))
    		{
    			an[x]=v;
    			break;
    		}
    	}
    }
    
    int main(void)
    {
    	scanf("%lld %lld",&n,&S);
    	for(int i=1;i<=n-1;i++)
    	{
    		ll x,y;
    		scanf("%lld %lld",&x,&y);
    		
    		add(x,y);
    		add(y,x);
    	}
    	
    	dfs(S);
    	get(S);
    	
    	for(int i=1;i<=n;i++)
    	{
    		printf("%d:",i);
    		
    		if(an[i])
    		{
    			printf("%d %d",std::min(zx[i],an[i]),std::max(zx[i],an[i]));
    		}
    		else printf("%d",zx[i]);
    		putchar(10);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    【小程序】订阅消息
    【小程序】轮播图
    【小程序】全局变量的设置、使用、修改、全局方法执行
    【RN】标题栏右边添加自定义按钮或加事件
    【RN】阴影react-native-shadow
    【vue】点击复制到剪贴板的方法( clipboard )
    Q-learning和Sarsa的区别
    Q-learning之一维世界的简单寻宝
    使用tensorflow时,关于GPU的设置
    安装Matlab出现弹出DVD1插入DVD2的提示怎么办?
  • 原文地址:https://www.cnblogs.com/jd1412/p/14066753.html
Copyright © 2020-2023  润新知