1021 Deepest Root

1021 Deepest Root (25 分)

A graph which is connected and acyclic can be considered a tree. The hight 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


题型分类:图的遍历

题目大意:给定N个结点和N-1条边,判断是否构成树,如果为树,找出使树高最大的根节点。根节点不唯一时,以从小到大的顺序输出。

解题思路:

1.判断是否构成一棵树有两种方法:方法①:选一个结点进行遍历,看是否能够遍历完整棵树;方法②:使用并查集的方法,判断两个结点的是否属于同一个集合。

2.寻找使树高最大的根节点,本题也有两种解法:方法α:对每个结点进行暴力求解,求出每个结点作为根节点时整棵树的树高;方法β:任意选取一个结点,对这个结点进行遍历,找出其遍历最深的叶子结点(可能不止一个),我们称其为集合A。从集合A中任意选取一个结点,再对这个选取的结点进行遍历,同样找出其遍历最深的叶子结点(可能不止一个),我们称这个集合为结合B。集合A∪集合B就是最终的答案。方法β需要一定的数学知识,可能并不好理解,但方法β的时间复杂度较小。

本次答案附上以上所有答案的代码。


代码一:方法①+方法α

#include <cstdio>
#include <vector>
#include <cstring>

using namespace std; 

const int maxn = 10010;

vector<int> Adj[maxn];
vector<int> deepestRoot;
bool visited[maxn];
int maxHigh = -1, nowHigh = 0;

void DFS(int v, int high);

int main(int argc, char** argv) {
	int N;
	scanf("%d", &N);
	for(int i = 1; i <= N - 1; i++){
		int v1, v2;
		scanf("%d %d", &v1, &v2);
		Adj[v1].push_back(v2);
		Adj[v2].push_back(v1); 
	}
	/*查看是否连通*/ 
	int cnt = 0;
	memset(visited, false, sizeof(visited));
	for(int i = 1; i <= N; i++){
		if(visited[i] == false){
			DFS(i, 0);
			cnt++;
		}
	}
	if(cnt > 1){ //表示不连通 
		printf("Error: %d components\n", cnt);
		return 0;
	}
	for(int i = 1; i <= N; i++){ //对每个结点进行遍历,将最深的结点纳入答案 
		memset(visited, false, sizeof(visited));
		nowHigh = 0;
		DFS(i, 0);
		if(nowHigh > maxHigh){ //该结点为根时树高更大 
			maxHigh = nowHigh;
			deepestRoot.clear();
			deepestRoot.push_back(i); 
		}
		else if(nowHigh == maxHigh){
			deepestRoot.push_back(i); 
		}
	}
	for(int i = 0; i < deepestRoot.size(); i++){
		printf("%d\n", deepestRoot[i]);
	}
	return 0;
}

void DFS(int v, int high)
{
	if(high > nowHigh){
		nowHigh = high;
	}
	visited[v] = true;
	for(int i = 0; i < Adj[v].size(); i++){
		if(visited[Adj[v][i]] == false){
			DFS(Adj[v][i], high + 1);
		}
	}
}

代码二:方法②+方法β

#include <cstdio>
#include <set>
#include <vector>
#include <cstring>

using namespace std;

const int maxn = 10010;

vector<int> Adj[maxn];
vector<int> temp; //临时存储集合A和集合B的结点 
set<int> deepestRoot;
bool visited[maxn];
int father[maxn], isRoot[maxn];
int maxHigh = -1;

void init(int n);
void Union(int a, int b);
int findFather(int x);
int calBlock(int n);
void DFS(int v, int high);

int main(int argc, char** argv) {
	int N;
	scanf("%d", &N);
	init(N); //并查集初始化 
	for(int i = 1; i <= N - 1; i++){
		int v1, v2;
		scanf("%d %d", &v1, &v2);
		Adj[v1].push_back(v2);
		Adj[v2].push_back(v1);
		Union(v1, v2);
	}
	int block = calBlock(N);
	if(block != 1){
		printf("Error: %d components\n", block);
	}else{
		DFS(1, 1);
		for(int i = 0; i < temp.size(); i++){
			deepestRoot.insert(temp[i]);
		}
		int newStart = temp[0]; //选取集合A中的一个结点作为根进行遍历 
		maxHigh = -1; //初始化工作 
		memset(visited, false, sizeof(visited));
		temp.clear();
		DFS(newStart, 1);
		for(int i = 0; i < temp.size(); i++){
			deepestRoot.insert(temp[i]);
		}
		for(set<int>::iterator it = deepestRoot.begin(); it != deepestRoot.end(); it++){
			printf("%d\n", *it);
		}
	}
	return 0;
}

void init(int n){
	for(int i = 1; i <= n; i++){
		father[i] = i;
	}
}

void Union(int a, int b){
	int faA = findFather(a);
	int faB = findFather(b);
	if(faA != faB){
		father[faA] = faB;
	}
}

int findFather(int x){
	int a = x;
	while(x != father[x]){ //找到根节点x 
		x = father[x];
	}
	while(a != father[a]){ //路径压缩 
		int z = a;
		a = father[a];
		father[z] = x;
	}
	return x;
}

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;
}

void DFS(int v, int high){
	visited[v] = true;
	if(maxHigh < high){
		temp.clear();
		temp.push_back(v);
		maxHigh = high;
	}else if(maxHigh == high){
		temp.push_back(v);
	}
	for(int i = 0; i < Adj[v].size(); i++){
		if(visited[Adj[v][i]] == false){
			DFS(Adj[v][i], high + 1);
		}
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值