您的位置:首页 > 文旅 > 美景 > 网络管理系统提供网络管理需要的大量运算和记忆资源_泉州网红_在线一键建站系统_汕头搜索引擎优化服务

网络管理系统提供网络管理需要的大量运算和记忆资源_泉州网红_在线一键建站系统_汕头搜索引擎优化服务

2024/12/22 18:09:25 来源:https://blog.csdn.net/m0_73904148/article/details/144238382  浏览:    关键词:网络管理系统提供网络管理需要的大量运算和记忆资源_泉州网红_在线一键建站系统_汕头搜索引擎优化服务
网络管理系统提供网络管理需要的大量运算和记忆资源_泉州网红_在线一键建站系统_汕头搜索引擎优化服务

目录

题目解析

算法原理

图的存储

算法实现 


题目解析

题目解析 

给定一颗树,树中包含n个结点(编号)和n-1条无向边。请找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。


什么是重心? 

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


补充:树和图的关系 

树本质上是一种特殊的图,它对应的是无环连通图。也就是说,在树中任意两个节点之间都存在路径(连通性),并且不存在回路(无环)。

树与一般图相比,有着如下性质:

  • 一般的图可以有环,节点之间的连接关系比较复杂。比如在一个有向图中,边是有方向的,从一个节点到另一个节点的路径可能受到边的方向限制;而树作为无向图,边没有方向限制,并且由于其无环的特性,从任意节点到另一个节点的路径是唯一的。
  • 从连通性角度看,树是保持图连通的最小结构。如果从树中去掉任何一条边,就会导致图不再连通,会划分出两个或更多的连通分量;而对于一般的连通图,可能存在冗余的边,去掉某些边后仍然可以保持连通。

重心举例

如下图,我们得到一个树结构:

 

若我们删除节点1,那么我们会得到三个连通图,如下:

  • 所谓的各个连通块中节点的最大值,指的是连通块之间的对比,在上图中,连通块节点数量最大值是4,即中间的连通块节点数量

重心是指我们遍历这张图中所有的节点,并尝试删除节点后所形成连通块节点数量的最大值是最小的。

举一个非重心的例子:如下图

 我们尝试删除节点2后,所形成的连通图一共有如下3个:

 很明显,连通块节点数量的最大值是6。

而尝试删除1的连通块节点数量的最大值是4,所以2一定不是树的重心

实际上,在上图中一共存在着两个重心,它们分别是1和4,感兴趣可以自己尝试算算


树的重心的性质 

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

算法原理

我们要求得一棵树得重心,那么较为简单得一种方式是尝试计算每一个节点如果被删除后,所得的连通块数量的最大值,并取计算完后的最小值。

下图删除节点4为例,思考一下,若删除节点4后,所形成的连通块有哪些?

 

很明显,连通块只会出现在两种情况:

  • 根节点为4的树的所有子树都是连通块
  • 整棵树的根,刨除4这个子树所形成的连通块

那么如何获得根节点为4的树的所有子树的节点数量,以及4所对应的树的节点数量呢

我们只需DFS即可:

  • 遍历到9,9无子树,向上返回1
  • 遍历到3,3的子树的连通块节点数量为1,那么3对应的连通块节点数量就为2,3向上返回2
  • 遍历到6,6无子树,向上返回1
  • 遍历到4,4的子树的连通块节点数量为2+1=3,所以4对应的连通块中节点数量为3+1=4(包含4自身)

如何获得整棵树的根(抛出删除节点4对应子树)的连通块数量呢?

直接通过n-i计算即可

其中n表示的是总的节点数量,在上图中n即为9。

其中i表示的是以被删除节点为根的子树的连通块数量,在上图中i即为4


图的存储

图的存储我们采用邻接表

  • 邻接表是图(包括有向图和无向图)的一种链式存储结构。它主要用于存储图中节点之间的连接关系。
  • 对于图中的每个顶点,都有一个单链表,链表中的节点存储了与该顶点相邻接的顶点信息

举例 

假设,值为1的点指向了2、4、7。那么我们只需要构建一个单链表,表头节点是1,其余的表示的是1指向它

若每一个节点都有一张单链表,用来表示它所指向的元素,那么该存储结构我们称之为邻接表

若是无向图,那么当1指向2时,还需要添加一条2指向1的边


单链表的实现

#include <iostream>
using namespace std;const int N = 1e5 + 10;
int ne[N], e[N], idx;
//ne存储的是next指针指向的节点编号
//e存储的是节点编号所对应的值
//idx用于分配一个唯一的节点编号int main()
{int n; //n是节点的数量cin >> n;//下标为0的点是头节点ne[0] = -1;//初始化单链表,-1来表示空节点idx = 1; //节点编号从1开始,因为0已经分配给头节点了for (int i = 0; i < n; ++i){int x;cin >> x;e[idx] = x; //分配一个节点编号给x ne[idx] = ne[0];//当前节点指向头节点指向的节点ne[0] = idx++;//头节点指向当前节点}for (int i = ne[0]; i != -1; i = ne[i]) printf("%d ", e[i]);return 0;
}

邻接表的实现 

#include <iostream>
using namespace std;const int N = 1e5 + 10;int h[N], ne[N], e[N], idx;void add(int a,int b)
{//1 3 表示1指向3,即h[1]头插3//添加一条a->b的边e[idx] = b;ne[idx] = h[a];h[a] = idx++;
}int main()
{int n; //边的数量cin >> n;memset(h, -1, sizeof h); //初始化邻接表for (int i = 0; i < n; ++i){int a, b;cin >> a >> b;add(a, b), add(b, a);}return 0;
}

算法实现 

第一步:根据题目要求,我们首先把输入进来的无向图保存起来,题目的输入如下:

  • 第一行:输入的是n
  • 第2-n行:输入两个数字a,b。表示a和b之间有一条无向边 

具体代码参考邻接表的实现

第二步:深度优先遍历

dfs中有一个参数u在dfs期间我们也一并更新最终的结果,定义变量ans表示最终的返回值

dfs函数要完成的功能是获得以u为根的子树形成的连通块节点数量,并更新根节点ans

注意:由于每一个节点我们只需要遍历一次,所以我们定义一个bool数组st,若st[i]=true,表示该节点已经被遍历过了,反之未遍历过

#include <iostream>
using namespace std;const int N = 1e5 + 10;int h[N], ne[N], e[N],st[N], idx;
int ans = N,n; //n表示边的数量 ,ans表示删除结果void add(int a,int b)
{e[idx] = b;ne[idx] = h[a];h[a] = idx++;
}
int dfs(int u)
{int res = 0; //res表示删除点u后连通块节点数量最大值int sum = 1; //sum表示要返回给上一层的以u为根的子树节点数量,初始化为1因为至少有u一个节点for (int i = h[u]; i != -1; i = ne[i]){int j = e[i];if (!st[j]){st[j] = true;int s = dfs(j);res = max(res, s); //连通块节点数量最大值可能出现在子树中sum += s;st[j] = false;}}res = max(res, n - sum); //最大值可能出现在根节点抛去u子树后的连通块节点数量ans = min(res, ans); //u可能是重心,更新一下return sum;
}
int main()
{cin >> n;memset(h, -1, sizeof h); //初始化邻接表for (int i = 0; i < n-1; ++i){int a, b;cin >> a >> b;add(a, b), add(b, a);}dfs(1);printf("%d", ans);return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com