图(树的重心)

在上正题之前咱们就先浅浅的介绍一下:

树是一种特殊的图,一个无环连通图

图:有向图、无向图

有向图:a--->b

无向图a---b,既可以从a到b,也可以从b到a,所以无向图也可以称为是特殊的有向图

可以构建类似于有向图,a—>b,b—>a;

有向图的存储:

①邻接矩阵:不能存储重边,比较浪费空间,适合存稠密图

②邻接表:单链表:每一个结点都开一个单链表(存这个点可以走到哪),比如:

 

 

 插入元素到单链表:

一般插入元素都是插到头节点的位置:时间复杂度O(1)

 

 

 插入代码:

复制代码
#include<iostream>
#include<cstring> 
using namespace std;
const int N=110;
int h[N],e[N],ne[N],idx;//有n个结点有n个头结点
void add(int a,int b)//插入一个由a指向b的 
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
} 
int main(){
    memset(h,-1,sizeof(h));//初始化头节点为-1 
}
复制代码

树和图的遍历:

深搜:

 

 

 宽搜:一层一层来搜:(数字代表层数)

 

 

 深搜和宽搜每个点就只能遍历一次

复制代码
#include<iostream>
#include<cstring> 
using namespace std;
const int N=110;
int h[N],e[N],ne[N],idx;//有n个结点有n个头结点
bool st[N];//进行标记 
void add(int a,int b)//插入一个由a指向b的 
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
}
void dfs(int u)
{
    st[u]=true;//这个点已经被用过了
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];//当前链表里的结点对应图里面的点的编号
        if(!st[j]) dfs(j);//如果这个点没有遍历过,就一直往下搜,一条路走到黑
        //每个点就走过一次,所以用不着回溯(恢复现场) 
    } 
}
int main(){
    memset(h,-1,sizeof(h));//初始化头节点为-1 
}
复制代码

下面用以上的板子来写题:

给定一颗树,树中包含 nn 个结点(编号 1n1∼n)和 n1n−1 条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式

第一行包含整数 nn,表示树的结点数。

接下来 n1n−1 行,每行包含两个整数 aa 和 bb,表示点 aa 和点 bb 之间存在一条边。

输出格式

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

数据范围

1n1051≤n≤105

输入样例

9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

输出样例:

4

这题看着让人头晕,但模拟一下样例,就可以知道要做什么了

 

 

 

 看这个样例,就知道要解决两个问题:

①如何求删去一个数,下面的连通块

②删去的这个点上面一圈连通块点的数量

令一个点为头节点(其实也就是删掉的那个点啦),用dfs进行深搜,就能知道下面的点数有多少

如何求上面:dfs只能往下不能往上,那把令的那个头节点删去,下面各个连通块的数量加起来,用总数来减就ok了

然后具体怎么实现呢:

就是枚举各个点的最大值,求他们中的最小的就好了

复制代码
#include<iostream>
#include<cstring> 
using namespace std;
const int N=100010,M=N*2;
int h[N],e[M],ne[M],idx;//有n个结点有n个头结点
bool st[N];//进行标记 
int ans=N;//存的是最小的最大值 
int n;
void add(int a,int b)//插入一个由a指向b的 
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
    idx++;
}
int dfs(int u)//返回的是以u为根中子树的数量连通块的最大值 
{
    st[u]=true;//这个点已经被用过了
    int sum=1,res=0;//sum为当前这个子树的大小(本身算上所以初始值为1), res为删掉那个点后每一个连通块中的最大值
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];//当前链表里的结点对应图里面的点的编号
        if(!st[j]) //如果这个点没有遍历过,就一直往下搜,一条路走到黑
        {
            int s=dfs(j);//当前连通块中的数量 
            res=max(res,s);//取最大值 
            sum+=s;//s只是一部分,需要求以u为根节点子树的数量,就是把局部的s加上去 
        }
        //每个点就走过一次,所以用不着回溯(恢复现场) 
    } 
    res=max(res,n-sum);//各个连通块中数量的最大值 
    ans=min(ans,res);//全部最大值的最小值 
    return sum;
}
int main(){
    memset(h,-1,sizeof(h));//初始化头节点为-1 
    cin>>n;
    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;
}
复制代码

 **************************************我是分割线~

哈喽,我又来二刷了,上面的代码解释有误,别看,别看,别看(重要的事说三遍)

复制代码
void dfs(int u)//以u为根节点 
{
    h[u]=true;//这个用过了 
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];//if不符合时就转到下一个结点,以u开头的 
        if(!st[j]) dfs(j);//往下一直搜 
    }
}
int main(){
    
}
/*
正好符合树的重心的题目: 
如果有符合的就一直往下搜,没有符合的以u为根节点找另一个分支 
总思路:找一个点为根节点连通块的最大值,然后,找出所有点(树里面的)求最大值的最小值 
*/
复制代码

以上是代码的一个思路

 

 

 

复制代码
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10,M=N*2;
int h[N],e[M],ne[M],idx;//idx为使用到点的下标 
int ans=N;//全局的答案,最小的最大值,在遍历过每个顶点之后
bool st[N];
int n; 
void add(int a,int b)//构建邻接表 
{
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
int dfs(int u)//以u为树根的连通块
{
     st[u]=true;//使用了之后需要标记
     int sum=1,res=0;
     for(int i=h[u];i!=-1;i=ne[i])
     {
         int j=e[i];
         if(!st[j])//如果没有被标记过 
         {
             int s=dfs(j);
             //一旦返回,s就等于返回值
             res=max(res,s);
             sum+=s;
        }
    }
    res=max(res,n-sum);
    ans=min(ans,res);
    return sum;
} 
int main(){
    cin>>n;
    memset(h,-1,sizeof(h));//初始化全部为-1 
    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;
}
复制代码

 

posted @   小志61314  阅读(103)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示