2020/10/26 树的重心

题目

给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式
第一行包含整数n,表示树的结点数。
接下来n-1行,每行包含两个整数a和b,表示点a和点b之间存在一条边。

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

数据范围
1≤n≤105

输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4

dfs模板

变量定义

  • int h[i]:编号为i的头指针
  • int e[i]:指针i所表示的编号
  • int ne[i]:指针i的下一个指针
  • boolean st[i]:编号为i是否被使用过
  • index:用来开辟空间为新建的结点(这个并不是编号)
  • res: 表示所求的结果,在递归过程不断迭代.

模板

dfs(int k){//搜索编号k
st[k]=true;
for(int i=h[k];i!=0;i=ne[i]){//找到k的子节点
int j=e[i];
dfs(j);//搜索每一个k的子节点
}
}

源代码

import java.util.*;
class Main{
    static int N=100010;
    static int index=0;
    static int[] e=new int[2*N];
    static int[] ne=new int[2*N];
    static int[] h=new int[N];
    static int ans=N;
    static boolean[] st=new boolean[N];
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=n-1;
        while(m-->0){
            int a=sc.nextInt();
            int b=sc.nextInt();
            add(a,b);
            add(b,a);
        }
        dfs(1,n);
        System.out.println(ans);
    } 
    public static void add(int a,int b){
        index++;
        e[index]=b;
        ne[index]=h[a];
        h[a]=index;
    }
    public static int dfs(int k,int n){//返回:编号为k的结点下有多少子节点(包扩自己),ps这个编号不是index
        st[k]=true;
        int sum=1;
        int res=0;
        for(int i=h[k];i!=0;i=ne[i]){
            int j=e[i];
            if(st[j]==false){
                int s=dfs(j,n);
                sum+=s;
                res=Math.max(res,s);
            }
        }
        res=Math.max(res,n-sum);
        ans=Math.min(ans,res);
        return sum;
    }
}

代码自问自答

Q:为什么e[]/ne[]数组的空间是2N,h[]/st[]数组的空间是N?
A:因为最多一共有N个节点,每个节点配一个链,因此头结点就是N个,而st[]数组表示的是某一个编号的结点是否被遍历过,因此主要N个即可.
但是由于是无向图,因此边的个数其实是向量的两倍,而其实每建一个向量,都需要开辟一个节点(也就是index++)来存储,最多就是2N个节点. 换句话来说,每一个有向边就需要一个节点来存储.一个无相边是两个有向边.

Q:为什么要标记结点被遍历过呢?
A:主要问题出在无向图!因为无向图相当于两个有向图,而如果不标记节点的话,很有可能在遍历时在两个结点间不停的遍历.

Q:为什么dfs函数返回的值不是res而是sum呢?
A:这是一种特别的设计方式,递归函数的返回值并不是自己想要的答案,但是在递归函数的递归过程中,答案在不停的迭代,递归完成时,只需要输出迭代后的答案即可.
本题的设计方式:
函数返回的值是sum:当前编号的结点下所有结点的个数.求法是遍历当前节点的所有未标记的子节点,调用递归函数返回每个子节点的个数,并将这些值加和存入sum变量中.
同时为了找到删除当前节点的各个部分的结点个数的最大值(其实就是找到子节点个数和n-sum的较大值),我们用res变量存储每个遍历子节点过程中的较大值,当把所有子节点都遍历到,res保存的其实就是这些子节点中,最多的结点数了.
之前存储的sum值就是为了帮助完成最后一步—n-sum和res值的较大值赋给res,自此,res变量保存的就是删除结点k的子节点个数的最大值.
又因为我们要看删除各个结点的res的最小值,因此我们又定义了ans变量在递归函数中,这样当所有递归函数结束时,ans就是我们想要的最终解.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值