分析
知识点:并查集 + 深度优先搜索
本题首先使用并查集检验该图是否是连通图,如果不是连通图,输出连通子图个数。如果是连通图,则使用深度优先搜索。
题目中要求以某节点为树根,树的最大深度,实质上是求从某节点出发,能达到的最远距离。那么进行一次循环,对每一节点,都以它为起点做深度优先搜索得到最大深度,找到能够有最大的最大深度的根节点。
代码
如果对并查集和DFS不熟练的小伙伴建议看看王道论坛的《机试指南》,里面对必须要掌握的基础算法做了详细的说明和例题分析。
这题的test case不算坑,一遍过啦~
#include<stdio.h>
#include<vector>
using namespace std;
vector<int> edges[10001]; // 邻接链表保存图
bool mark[10001]; // 深度优先搜索时会用到,mark[i]=true代表编号为i的节点已走过
int Tree[10001]; // 并查集求连通子图个数时会用到,Tree[i]代表编号为i的节点的双亲节点,若为-1,则表示该节点不存在双亲节点,为根
int bestLevel = 0; // 记录最大的最大深度
vector<int> bestRoot; // 可能有多个节点都可达到最大的最大深度
int findRoot(int x) // 查找某个节点所在树的根节点,并查集
{
if (Tree[x] == -1)
{
return x;
}
else
{
int tmp = findRoot(Tree[x]);
Tree[x] = tmp;
return tmp;
}
}
int DFS(int x, int level) // 深度优先搜索,从x节点开始,level为x节点此时的深度,返回经过x的这棵树的最大深度
{
int currentLevel = level;
for (int i = 0; i < edges[x].size(); i++) // 对x的且未被走到过的邻点进行递归
{
if (mark[edges[x][i]] == false)
{
mark[edges[x][i]] = true;
int tmpLevel = DFS(edges[x][i], currentLevel + 1);
if (tmpLevel > level)
{
level = tmpLevel;
}
mark[edges[x][i]] = false;
}
}
return level;
}
int main()
{
int N;
scanf("%d", &N);
for (int i = 1; i <= N; i++) // 初始化
{
Tree[i] = -1;
mark[i] = false;
edges[i].clear();
}
for (int i = 0; i < N - 1; i++) // 并查集
{
int a, b;
scanf("%d%d", &a, &b);
edges[a].push_back(b);
edges[b].push_back(a);
a = findRoot(a);
b = findRoot(b);
if (a != b)
{
Tree[a] = b;
}
}
int components = 0;
for (int i = 1; i <= N; i++)
{
if (Tree[i] == -1)
{
components++;
}
}
if (components > 1)
{
printf("Error: %d components\n", components);
}
else
{
for (int i = 1; i <= N; i++) // 将每个节点都作为根节点,检验哪个节点做根节点时能有最大的最大深度
{
mark[i] = true;
int level = DFS(i, 0); // 节点i做根节点,起始层数为0,返回此时的最大深度level
if (level > bestLevel) // 如果此时的最大深度大于已有的最大的最大深度,则更新
{
bestLevel = level;
bestRoot.clear();
bestRoot.push_back(i);
}
else if (level == bestLevel) // 如果此时的最大深度等于已有的最大的最大深度,将节点i添加到bestRoot中
{
bestRoot.push_back(i);
}
mark[i] = false;
}
for (int i = 0; i < bestRoot.size(); i++)
{
printf("%d\n", bestRoot[i]);
}
}
return 0;
}