1021 Deepest Root(dfs 求强连通分量的个数(并查集))

本文探讨了一种算法,用于在给定的无向图中找到能够构成最高树的根节点,即所谓的最深根。文章详细解释了如何通过深度优先搜索(DFS)和并查集来解决这个问题,同时处理了可能存在的多个连通组件的情况。

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

超时代码 + 错误的判断题意。。

具体代码:

/**
内存超限
但是遇到几个粗心的错误,
1 . dfs 没有设置vis[max][max]数组 标记访问 导致了死循环
2.  多组数据输入时,没有重置vis数组  
对于思维上的错误:
最开始的理解是 当 邻接表 edge[i].size为空时 就判断 最深的结点,理解错误,在无向图中 2 5 有边 , 5 2 也会
存在边 edge[i].size不会为空,  
修改:
当每次for循环结束 就判断一次 高度, 
**/ 
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxsize = 1e4 + 5;
int n, hmax, to, from, set[maxsize]; 
vector<int> edge[maxsize], temp;
bool isTree = true, visit[maxsize], vis[maxsize][maxsize];
int find(int i){
	return set[i] == i ? i : set[i] = find(set[i]);
}
void merge(int x, int y){
	int faX = find(x);
	int faY = find(y);
	if(faX == faY){
		isTree = false;
		return;
	}
	set[faX] = faY;
}
void dfs(int u, int h){ //形成死循环的原因是每有将已经走过的点标记, 有向图可以设置为 visit[i][j]来表示 多组输入时,还需初始化vis数组 
//	cout << "u: " << u  << endl;
//	cout << edge[u].size() << endl;
	for(int i = 0; i < edge[u].size(); i++){
			if(!vis[u][edge[u][i]]){
				vis[u][edge[u][i]] = vis[edge[u][i]][u] = true;
				dfs(edge[u][i], h + 1); 
			}
	}
	
//	cout << "h : " << h << "hmax : " << hmax << endl;
	if(h == hmax){
		temp.push_back(u);
	}else if(h > hmax){
		hmax = h;
		temp.clear();
		temp.push_back(u);
	}
}
void init(){
	for(int i = 1; i <= n; i++){
		set[i] = i;
		for(int j = 1; j <= n; j++)
			vis[i][j] = false;
	}
}
int main(){
	scanf("%d", &n);
	init(); //初始化并查集 
	for(int i = 0; i < n - 1; i++){
		if(!isTree) 
			break;
		else{
			scanf("%d%d", &from, &to);
			merge(from, to);
			edge[from].push_back(to);
			edge[to].push_back(from);
		}
	}
	if(isTree){
		for(int i = 1; i <= n; i++){
			init();//初始化vis数组	
			dfs(i, 1);
		} 
			
//		cout << temp.size() << endl;
		sort(temp.begin(), temp.end());
		for(int i = 0; i < temp.size(); i++){
			if(!visit[temp[i]]){
				visit[temp[i]] = true;
				printf("%d\n", temp[i]);
			}
		}
	}else
		printf("Error: K components\n");
	return 0;
}

 

 

/**
本题题意:
     给定n个结点(编号从1 - n) 给出 n - 1 条边,根据n - 1条边 需要求出 是否可以构建成一棵树(只有一个集合)(只存在一个强连通分量)
(最初的理解认为是 构建一棵树 是不能存在回路(In case that the given graph is not a tree, print “Error: K components”) 当不是一棵树
需要输出有几个强连通分量.
本题思路:
     判断回路, (可以用并查集), 判断强连通分量的个数可以用并查集 也可以用dfs判断    ,无向图的dfs的 visit标记并不用设计出二维, 只用一维
     就够用了,
     1.找到一颗树的最深结点,  一开始的思路是 将每个结点当作顶点, 将每个结点作为起点, 判断树的最大高度 有多少个这样的起点,然后将这些点
     装入一个 vector中 , 并排序, 但是此时需要去重, 直接使用一个vis数组进行标记, 输出过一次的元素就不用输出了 , 当然用set存储就不用进
     行去重 和 排序的操作
     2. 本题也存在更简单的思路:
             任意从图中一个点进行遍历到最深的点集s  然后用s集合的其中一个点再次dfs 找到 最深的点集 s2, 此时s1, s2的并集就是最深的根
     3.本题需要注意的是: 进行多次输入时 需要初始化判断的条件 . eg : visit[] 数组, temp数组(存放最深的root结点),  
 
**/

 

#include<iostream>
#include<set>
#include<vector>
using namespace std;
const int maxsize = 1e4 + 5;
int n, hmax, floor, cnt, from, to;//temp 存放最深的root结点 floor为第一次dfs找到的最深结点 , cnt记录强连通分量的个数 (集合的个数) 
vector<vector<int>> edge;
vector<int> temp;
set<int> s;
bool visit[maxsize];
void dfs(int u, int h){
	visit[u] = true;
	for(int i = 0; i < edge[u].size(); i++){
		if(visit[edge[u][i]] == false)
			dfs(edge[u][i], h + 1); 
	}
	if(h > hmax){
		temp.clear();
		temp.push_back(u);
		hmax = h;
	}else if(h == hmax)
		temp.push_back(u);
}
int main(){
	scanf("%d", &n);
	edge.resize(n + 1); 
	for(int i = 0; i < n - 1; i++){
		scanf("%d%d", &from, &to);
		edge[from].push_back(to);
		edge[to].push_back(from);
	}
	for(int i = 1; i <= n; i++){
		if(visit[i] == false){
			dfs(i, 1);
			cnt++;
			if(temp.size() != 0)
				floor = temp[0];
			for(int j = 0; j < temp.size(); j++)
				s.insert(temp[j]);
		}
	}
	if(cnt >= 2)
		printf("Error: %d components\n", cnt);
	else{
		temp.clear();
		hmax = 0;
		fill(visit + 1, visit + n + 1, false);
		dfs(floor, 1);
		for(int i = 0; i < temp.size(); i++){
			s.insert(temp[i]);
		}
		for(auto it = s.begin(); it != s.end(); it++){
			printf("%d\n", *it);
		}
	}		
	return 0;
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值