树的重心——DFS求解

定义:

树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小



性质:

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻


重心的求法

删除重心后最大连通块的结点数最小,那么就要对每个结点,以其为根,找出其所有子树的最大结点数,然后找出 子树最大结点数最小的那个结点即是重心

那么怎么一次DFS在O(N)的时间就找出重心呢?

随意选择一个根结点,进行DFS,对于 某个结点u ,每次向下遍历,可以得出 u向下遍历的各个子树的结点数 ,但是如果以u作为根结点话,还有 一颗向上遍历的子树结点数 没有考虑,但是可以发现 这棵向上遍历的子树的结点数= N - 向下遍历的各子树结点数之和 -1


具体实现:

int min_balance=INF;   //删除重心后最大连通块的数量
int min_node;          //重心
int dfs(int u)     //dfs每次返回以u为根的子树的结点数量
{
    vis[u]=true;
    int sum=0;     //向下遍历的各子树结点数之和
    int max_subtree=0;   //最大子树结点数
    for(int i=head[u];i!=-1;i=e[i].next)   //向下遍历
    {
    	int v=e[i].v;
        if(vis[v])
            continue;
        int t=dfs(v);   //得到以v为根结点的子树的结点数
        sum+=t;
        max_subtree=max(max_subtree,t);
    }
    max_subtree=max(max_subtree,N-sum-1);   //考虑向上遍历的子树的结点数
    if(max_subtree<min_balance)   //更新重心
    {
        min_balance=max_subtree;
        min_node=u;
    }
    return sum+1;    //+1为结点u本身
}


例题:

Balancing Act POJ - 1655
给出一颗有N个结点的树,求出其重心删除重心后的最大连通块的结点数

AC代码:

#include
#include
#include
#include
#include
#include
#include
#define PII pair
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
const int maxn=2e4+50;
struct edge
{
    int u;
    int v;
    int next;
}e[maxn*2];
int head[maxn],cnt,N;
void addedge(int u,int v)
{
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
bool vis[maxn];
int min_balance=INF;   //删除重心后最大连通块的数量
int min_node;          //重心
int dfs(int u)     //dfs每次返回以u为根的子树的结点数量
{
    vis[u]=true;
    int sum=0;     //向下遍历的各子树结点数之和
    int max_subtree=0;   //最大子树结点数
    for(int i=head[u];i!=-1;i=e[i].next)   //向下遍历
    {
    	int v=e[i].v;
        if(vis[v])
            continue;
        int t=dfs(v);   //得到以v为根结点的子树的结点数
        sum+=t;
        max_subtree=max(max_subtree,t);
    }
    max_subtree=max(max_subtree,N-sum-1);   //考虑向上遍历的子树的结点数
    if(max_subtree<min_balance)   //更新重心
    {
        min_balance=max_subtree;
        min_node=u;
    }
    return sum+1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        scanf("%d",&N);
        for(int i=1;i<=N-1;i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        min_balance=INF;
        memset(vis,0,sizeof(vis));
        dfs(1);
        printf("%d %d\n",min_node,min_balance);
    }
    return 0;
}

你可能感兴趣的:(★Tips,★搜索,★图论)