目录
1,题目描述
Sample Input 1:
5
1 2
1 3
1 4
2 5
Sample Output 1:
3
4
5
Sample Input 2:
5
1 3
1 4
2 5
3 4
Sample Output 2:
Error: 2 components
题目大意
一棵树以不同的节点作为根节点时,树可能会有不同的高度,使树的高度达到最大值时的节点为Deepest Root。
输入
第一行:节点数目N(<=10^4,编号1-N)
N-1行:描述树中每条边连接的两个节点
输出
若为非连通图,输出Error: k components ,k即连通分量数目
否则,按序号从小到大,输出所有的Deepest Root;
2,思路
思路一
野蛮解法。先判断是否为连通图,若不是连通图,则对每一个节点调用一次dfs,保留并更新Deepest Root。
结果显示这种算法会有一个测试点超时。(还有一个段错误,可能是我自己代码的问题。。。)
思路二
参考柳神的解题思路。
两次dfs。第一次dfs保留所有的最高高度的节点,从中选择一个节点,进行第二次dfs,同样保留所有的最高高度节点。两个节点集合的并集,就是所求内容。 (很神奇!!!)
算法
- 第一次dfs算法过程中,用set<int> depth记录所有具有最高高度的节点;
- 判断是否还有节点未被访问(有多个联通分量),是的话只需要继续dfs并输出连通分量个数即可,否则继续;
- 将set<int> depth赋值给set<int> ans,并从ans中取一个元素(第一个就行)再次进行dfs,用set<int> depth记录所有具有最高高度的节点;
- 将ans与depth中的内容合并(求并集)即可;
段错误
- 若出现段错误,请检查在接收边的数据时,你的上限是<n还是<n-1。n的话,问题就在这里了。
3,代码
思路一
也把思路一的代码贴出来,有兴趣的同学可以看一下。(有一个测试点超时,还有一个段错误)
#include<iostream>
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
bool visited[10001];
bool graph[10001][10001];
vector<int> depth;
int temDeep = 0, deepest = 0, n = 0, num = 1;
void dfs(int start, int deep){
visited[start] = true;
for(int i = 1; i <= n; i++){
if(graph[start][i] == true && visited[i] == false) dfs(i, deep+1);
}
if(deep > temDeep) temDeep = deep;
}
int main(){
//#ifdef ONLINE_JUDGE
//#else
// freopen("1.txt", "r", stdin);
//#endif
scanf("%d", &n);
for(int i = 0; i < n; i++){
int a, b;
scanf("%d%d", &a, &b);
graph[a][b] = graph[b][a] = true;
}
for(int i = 1; i <= n; i++){
fill(visited, visited + 10001, false);
temDeep = 0;
dfs(i, 0);
for(int j = 1; j <= n; j++){
if(visited[j] == false){
num += 1;
dfs(j, 0);
}
}
if(num > 1){
printf("Error: %d components", num);
return 0;
}
if(temDeep > deepest){
deepest = temDeep;
depth.clear();
depth.push_back(i);
}else if(temDeep == deepest){
depth.push_back(i);
}
}
for(int i = 0; i < depth.size(); i++){
printf("%d\n", depth[i]);
}
return 0;
}
思路二
绝对清晰易懂!
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
bool visited[10001];
bool graph[10001][10001];
set<int> ans, depth; //ans最终结果 depth暂时存放具有最高高度的节点
int temDeep = 0, n = 0, num = 1; //tempDeep临时最大高度 n节点数目 num连通分量数
void dfs(int start, int deep){
visited[start] = true;
for(int i = 1; i <= n; i++){
if(graph[start][i] == true && visited[i] == false) dfs(i, deep+1);
}
if(deep > temDeep){
temDeep = deep;
depth.clear();
depth.insert(start);
}else if(deep == temDeep){
depth.insert(start);
}
}
int main(){
scanf("%d", &n);
for(int i = 0; i < n-1; i++){ //注意是n-1(n个节点 n-1条边)!!!
int a, b;
scanf("%d%d", &a, &b);
graph[a][b] = graph[b][a] = true;
}
fill(visited, visited + 10001, false);
dfs(1, 1);
for(int j = 1; j <= n; j++){
if(visited[j] == false){
num += 1;
dfs(j, 1);
}
}
if(num > 1){ //判断连通分量数目
printf("Error: %d components", num);
return 0;
}
ans = depth; //第一次dfs中具有相同最高高度的节点 存放至答案set中
depth.clear();
fill(visited, visited + 10001, false);
temDeep = 0;
dfs(*ans.begin(), 1); //在第一次dfs中具有相同最高高度的节点中 选择一个进行第二次dfs
for(auto it = depth.begin(); it != depth.end(); it++){// it != depth.end()
ans.insert(*it); //两个集合求并集(由于是set 直接插入即可)
}
for(auto it = ans.begin(); it != ans.end(); it++){
printf("%d\n", *it);
}
return 0;
}