1 题目
1021 Deepest Root (25分)
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 (≤10
4
) 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
2 解析
2.1 题意
给出N个结点和N-1条边,问它们是否能形成一颗N个结点的树,如果能,能输出能使得树高度最高的树根结点,如果不能输出“Error: 2 components”
2.2 思路
- 判断是否是图是否为一棵树———连通块只有一个,N个结点有N-1条边
2.2.1 思路一
- 1 用DFS遍历树,求出图的连通块个数
- 2 如果连通块大于1,则输出“Error: 2 components”
- 3 否则
- 3.1 DFS遍历树(每次遍历新结点时,清空上一个结点访问时是曾记录的否被访问的标记),求树最大的高度
- 3.2 DFS遍历树,某个结点求的高度等于最大高度时,记录该结点
-输出该结点
2.2.2 思路二
- 前提结论:
- 1 从任意结点出发进行遍历,得到的最深结点一定是所求根结点集合的一部分。
- 2 两次遍历结果的并集为所求的根结点集合
- 3 所有直径(使得树最深的根结点以及叶结点的长度)有一段公共区重合区间(或交与一个公共点)。
- 1 用并查集判断图是否连通(并查集求图的连通块数目)
- 2 DFS遍历图求图的最大高度以及能使得图达到最大高度的一个结点A
- 3 从结点A用DFS遍历图,求结点A出发能使得高度等于最大高度的结点
- 3 输出结点
3 参考代码
3.1 思路一
#include
#include
#include
#include
#include
using std::sort;
using std::vector;
const int MAXV = 10010;
vector<int> G[MAXV];
bool vis[MAXV] = {false};
int N;
int depthMax = INT_MIN;
int ans[MAXV];
int nodeCount = 0;
int tempDepth;
int block = 0;
void DFS(int u ,int depth){//求最大高度
vis[u] = true;
if(depth > depthMax){
depthMax = depth;
}
for (int i = 0; i < G[u].size(); ++i)
{
if(vis[G[u][i]] == false){
DFS(G[u][i], depth + 1);
}
}
}
void DFS2(int u ,int depth){//求能使树有最大高度的结点
vis[u] = true;
if(depth == depthMax){
tempDepth = depth;
}
for (int i = 0; i < G[u].size(); ++i)
{
if(vis[G[u][i]] == false){
DFS2(G[u][i], depth + 1);
}
}
}
void DFSTravel(int chose){
if(chose == 1){
for (int i = 1; i <= N; ++i)
{
memset(vis, false, sizeof(vis));
if(vis[i] == false){
DFS(i, 1);
}
}
}else{
for (int i = 1; i <= N; ++i)
{
tempDepth = 1;
memset(vis, false, sizeof(vis));
if(vis[i] == false){
DFS2(i, 1);
}
if(tempDepth == depthMax){
ans[nodeCount++] = i;
}
}
}
}
void DFSTravel2(){//求连通块的个数
for (int i = 1; i <= N; ++i)
{
if(vis[i] == false){
DFS(i, 1);
block++;
}
}
}
int main(int argc, char const *argv[])
{
scanf("%d", &N);
int e1, e2;
for (int i = 0; i < N - 1; ++i)
{
scanf("%d%d", &e1, &e2);
G[e1].push_back(e2);
G[e2].push_back(e1);
}
DFSTravel2();
if(block > 1){//如果不是树
printf("Error: %d components\n", block);
}else{//如果是树
DFSTravel(1);//求树的最大高度
DFSTravel(2);//求使得树有最大高的结点
sort(ans, ans + nodeCount);//结点排序
for (int i = 0; i < nodeCount; ++i)
{
printf("%d\n", ans[i]);
}
}
return 0;
}
3.2 思路二
#include
#include
#include
using std::sort;
using std::vector;
const int MAXV = 100010;
vector<int> G[MAXV];
int father[MAXV];
bool isRoot[MAXV];//记录每个结点是否为某个集合的根结点
int findfather(int x){
int a = x;
while(x != father[x]){
x = father[x];
}
while(a != father[a]){//路径压缩
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
void Union(int a, int b){
int faA = findfather(a);
int faB = findfather(b);
if(faA != faB){
father[faA] = faB;
}
}
void Init(int n){
for (int i = 1; i <= n; ++i)
{
father[i] = i;
}
}
int calBlock(int n){
int block = 0;
for (int i = 1; i <= n; ++i)
{
isRoot[findfather(i)] = true;
}
for (int i = 1; i <= n; ++i)
{
block += isRoot[i];
}
return block;
}
int maxH = 0;
vector<int> temp, ans;
void DFS(int u, int height, int pre){
if(height > maxH){
maxH = height;
temp.clear();
temp.push_back(u);
}else if(height == maxH){
temp.push_back(u);
}
for (int i = 0; i < G[u].size(); ++i)//遍历u的所有子结点
{
if(G[u][i] == pre) continue;//邻接表存无向图,需要跳过回去的边
DFS(G[u][i], height + 1, u);
}
}
int main(int argc, char const *argv[])
{
int n;
scanf("%d", &n);
Init(n);//初始化集合
int v1, v2;
for (int i = 0; i < n - 1; ++i)
{
scanf("%d%d", &v1, &v2);
G[v1].push_back(v2);
G[v2].push_back(v1);
Union(v1, v2);//合并e1、e2所在集合
}
int block = calBlock(n);
if(block != 1){//连通块个数大于1
printf("Error: %d components\n", block);
}else{
DFS(1, 1, -1);//从1号结点开始,初始高度为1
ans = temp;
DFS(ans[0], 1, -1);
for (int i = 0; i < temp.size(); ++i) {
ans.push_back(temp[i]);
}
sort(ans.begin(), ans.end());
printf("%d\n", ans[0]);
for (int i = 1; i < ans.size(); ++i)
{
if(ans[i] != ans[i - 1]){//重复编号不输出
printf("%d\n", ans[i]);
}
}
}
return 0;
}