图与树的深度遍历

在这里插入图片描述

 在这里插入图片描述

这里在讲一下重心的概念,就是每删掉一个结点,剩下的连同块中都取最大值,然后每个最大值比较,最小的那个就是重心

 本题思路:

在dfs的时候统计子树的大小

在这里插入图片描述

 假如这里我们要统计删掉4结点的连通块,这里有三部分(3,9)、(6)、(1,2,7,8,5)

3,6是4的子树,而9是三的子树,在递归的时候我们就可以统计出来,而上面一部分我们将他称为4的父连通块,我们可以用总数减去以4为根的子树大小(包括4)。就可以求出父连通块的数量


 这里要注意,其实从哪个点都可以开始枚举,因为这是一个无向图,都可以走通,但每个点最多走一次,所以要防止子结点又走回父节点


#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
const int M =2*N;
int n;
int e[M],head[N],ne[M],idx;
int ans=N;
bool st[M];//状态数组,防止子节点搜索父节点
void add(int a,int b)//构造邻接表(无向图)
{
    e[idx]=b,ne[idx]=head[a],head[a]=idx++;
}//头插法

//返回以u为根的子树大小
int dfs(int u)
{
    st[u]=true;//st标记当前点被搜过
    int res=0;//res为最大连通块的数量(删除某个结点后)
    int sum=1;//当前子树的大小算上根所以从1开始
    for (int i=head[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if (!st[j])
        {
            int s=dfs(j);
            res=max(res,s);
            sum+=s;
        }
    }
    res=max(res,n-sum);//删除某点后,判断子连通块和父连通块谁大
    ans=min(ans,res);//最小的最大值
    return sum;
}

int main()
{
    cin>>n;
    memset(head, -1, sizeof head);
    for (int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    dfs(1);
    cout<<ans<<endl;
    return 0;
}
# acwing 846. 树的重心

N = 100010
# 将重心删除后,剩余各个连通块中点数的最大值,先初始化为最大值
ans = N

def add(a,b):
    global idx

    e[idx] = b
    ne[idx] = h[a]
    h[a] = idx
    idx += 1

# 深度优先遍历一般都有个for循环,
# 然后再在for循环中深度遍历
def dfs(u):
    global ans

    # 标记访问过树节点u
    st[u] = True

    # sum:以u为根的树的节点数(包括u)
    # res:删除树节点u后,最大连通子图(子树)节点数
    sum, res = 1, 0

    # 找到存储与u节点相连的树节点的存储节点
    i = h[u]
    while i!=-1:
        # 树节点j为与树节点u直接相连的树节点
        j = e[i]
        # 如果树节点j没有被访问过
        if not st[j]:
            # 以树节点u的子节点j为根的树的大小
            s = dfs(j)
            # 求最大连通子图(子树)的大小
            res = max(res,s)
            # 先把这棵子树的节点数加上
            sum += s
        # 继续访问树节点u的下一棵子树
        i=ne[i]

    # 现在把以u为根的所有子树的连通数情况求出来了,但是还要将
    # 当前结果和整棵树除掉u这棵子树的数量进行对比
    res = max(res,n-sum)
    # 比较删除当前树节点的最大连通子图节点数和
    # 删除其它节点得出的最大连通子图节点数
    ans = min(ans,res)
    return sum

if __name__ == '__main__':
    h, e, ne, idx = [-1]*N, [0]*2*N, [0]*2*N, 0
    # 记录某个树节点是否被访问过
    st = [False]*N
    n = int(input())

    # 无向图,所以要从a->b,b->a都要插入一条边
    for i in range(n-1):
        a,b = map(int, input().split())
        add(a,b)
        add(b,a)

    dfs(1)
    print(ans)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值