• 树的重心


    先看题:P1395会议

    树的重心,就是在一棵树中拆掉一个点,把这棵树分成几个部分,使得最大的部分最小,亦即拆得均匀,这个拆掉的点就是树的重心。

    那么怎么求呢?我们可以从根结点开始dfs(什么?你说是无根树。那就定义节点1为根呗),返回值是这棵子树的大小。然后定义一个(mx)(mx=max{dfs( ext{该结点的子结点的})}) 。最后,在枚举完子节点后,再判断他父亲那一块的大小。然后记录值。
    现在唯一的问题就是父亲那块怎么解决呢?没错,用总结点数减去这棵子树的大小,就是他父亲那一块的大小。

    现在来解决第二问 ,这问其实可以在解决树的重心后直接dfs或bfs,然后就可以得出总的路程了。这一块就不用详细解释了。

    上代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,ans=50005,sum=50005,road;//n是总结点数,ans是树的重心的编号,sum是其最那块的大小,road是总路程
    int t[50005];//ti表示结点i到ans的距离。
    queue<int>q;//q用来后面bfs用
    struct graph
    {
    	int tot;
    	int dt[100005],nxt[100005];
    	int hd[50005];
    	void add(int x,int y)
    	{
    		tot++;
    		nxt[tot]=hd[x];
    		hd[x]=tot;
    		dt[tot]=y;
    	}
    }g;//graph是链式前向星。
    int dfs(int x,int fa)
    {
    	int k=0;//k代表其所以子树的大小
    	int mx=0;//mx是将这个点拆掉后最大那块的大小
    	for(int i=g.hd[x];i;i=g.nxt[i])//遍历所以点子节点
    	 if(g.dt[i]!=fa)//注意判该节点是否是x的父亲,不判会爆栈的
    	 {
    	 	int xx=dfs(g.dt[i],x);//记录这棵子树的大小
    		k+=xx;//加上这棵子树的大小
    		mx=max(mx,xx);//取最大值
    	 }
    	mx=max(mx,n-k-1);//最后判一下父亲那块,最后的-1是因为最开始没有把自己算进去
    	if(mx<sum||(mx==sum&&x<ans)) sum=mx,ans=x;//如果比当前方案更优,就取这个方案
    	return k+1;//返回整棵子树的大小
    }
    void bfs()
    {
    	q.push(ans);//记录最初的点
    	while(!q.empty())//如果队列不为空
    	{
    		int xx=q.front();//记录队首
    		q.pop();//弹出队首
    		for(int i=g.hd[xx];i;i=g.nxt[i])//遍历链接它的节点
    		{
    			int yy=g.dt[i];
    			if(t[yy]||yy==ans) continue;//如果这个点已经被遍历过,continue
    			t[yy]=t[xx]+1;//记录距离
    			road+=t[yy];//总距离要加上这个点的距离
    			q.push(yy);//放进队列
    		}
    	}
    	return ;
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<n;i++)
    	{
    		int from,to;
    		cin>>from>>to;
    		g.add(from,to);
    		g.add(to,from);
    	}//输入+存图,注意是双向边
    	dfs(1,0);//dfs,把1的父亲设为0,就是没有父节点了(因为没有节点0)
    	bfs();//求出树的重心后,求总路程
    	cout<<ans<<' '<<road;
    	return 0;
    }
    
  • 相关阅读:
    [TimLinux] Python 函数(2)
    [TimLinux] Python nonlocal和global的作用
    [TimLinux] Python 装饰器
    fragment+viepager 的简单暴力的切换方式
    EditText键盘弹出时,会将布局底部的导航条顶上去(解决方法之一)
    EditText取消自动调用键盘事件(方法之一)
    Fragment滑动切换简单案例
    ListAdapter列表适配器
    ListView列表的简单案例
    ViewPager图片切换的简单案例
  • 原文地址:https://www.cnblogs.com/mk-oi/p/13522653.html
Copyright © 2020-2023  润新知