原题干:
A graph which is connected and acyclic can be considered a tree. The height of the tree depends on the selected root. Now you are supposed to find the root that results in a highest tree. Such a root is called the deepest root.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (<=10000) which is the number of nodes, and hence the nodes are numbered from 1 to N. Then N-1 lines follow, each describes an edge by given the two adjacent nodes' numbers.
Output Specification:
For each test case, print each of the deepest roots in a line. If such a root is not unique, print them in increasing order of their numbers. In case that the given graph is not a tree, print "Error: K components" where K is the number of connected components in the graph.
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
题目大意:第一行给出图的顶点个数,接下来给出N-1行数据,每行两个正整数,代表这两个正整数代表的顶点是连接的
要求判断这个图是不是一个连通块。如果不是一个连通块,输出错误信息,并说明有几个连通块。如果是一个连通块,则判断这些顶点能表示的最大高度的树,有几种可能的根,并按照顺序输出(要保持顶点的连接状况)(博主语文不好,只能说成这样了)
这道题判断连通块可以使用并查集,亦可使用DFS。这里博主用的是DFS。至于找出最高的树的根,《算法笔记》上给出了一种很好的办法:
先随机找一个节点,沿着节点遍历,找到能到达最大高度的所有节点(记做集合A),接着在集合A中随机找一个节点,沿着它遍历,找到能到达的所有节点(记做集合B),这时,集合A和集合B就是所求树根的集合。
至于为什么,其实也很好理解。随机找一个节点,可能是中间的某个节点;遍历得到的集合A,是这个节点能到达的最大高度。所以A一定是一个"边缘性节点",最大高度的树一定是"边缘性节点到边缘性节点",而不可能是中间的某个节点。沿着A中的任意元素进行遍历,找到的集合B一定还是边缘性节点,故两者都是最大高度的树的根。(博主语文不好。。。只能讲成这样了)
至于排序去重,我们这里可以使用set容器来完成。代码里面有很详细的注释:
#include <iostream>
#include <vector>
#include <set>
using namespace std;
#define MAXN 10010
/**
* 全局变量区
* G为邻接表,tmp为存储临时结果的容器
* visited为标记数组,nodes_sum为节点总数,max_depth存放最大高度
*/
vector<int> G[MAXN], tmp;
bool visited[MAXN] = {false};
int nodes_sum, max_depth = 0;
/**
* 深度优先搜索方法
* 参数:起始节点、深度
*/
void DFS(int start, int depth){
if(depth > max_depth){ //如果深度大于最大深度
tmp.clear(); //清空临时容器
tmp.push_back(start);//将当前顶点压入容器
max_depth = depth; //更新最大深度值
}else if(depth == max_depth) tmp.push_back(start); //如果深度等于最大深度,将当前节点压入容器
visited[start] = true; //标记当前访问的节点为"已访问"
for(int i = 0; i < G[start].size(); i++){
int next = G[start][i];//下一个节点的编号
if(!visited[next]) DFS(next, depth + 1); //递归访问下一个节点
}
}
/**
* 计算连通块的方法
* 返回:连通块数量
*/
int calBlock(){
int cnt = 0;
for(int i = 1; i <= nodes_sum; i++){
if(!visited[i]){
cnt++;
DFS(i, 1);
}
}
return cnt;
}
int main(){
cin >> nodes_sum; //读入节点数量
for(int i = 1; i < nodes_sum; i++){ //依次读入顶点
int tmp1, tmp2;
cin >> tmp1 >> tmp2;
G[tmp1].push_back(tmp2);
G[tmp2].push_back(tmp1); //写入邻接表
}
int block_sum = calBlock(); //计算连通块数量
if(block_sum > 1){ //连通块数量大于1,输出错误信息,退出
cout << "Error: " << block_sum << " components" << endl;
exit(0);
}
set<int> result; //存放结果的set容器
for(int i = 0; i < tmp.size(); i++) result.insert(tmp[i]); //将当前结果存入容器病自动排序
fill(visited, visited + nodes_sum + 1, false); //刚刚DFS已经污染了visited,这里要重新赋值
DFS(tmp[0], 1); //遍历
for(int i = 0; i < tmp.size(); i++) result.insert(tmp[i]); //存入容器并排序
for(set<int>::iterator i = result.begin(); i!= result.end(); i++){ //枚举输出
cout << *i << endl;
}
return 0;
}