PAT 1021

1021 Deepest Root

思路:

1.并查集求联通数

2.dfs找deepest root,并将root添加到结果中

代码v1

#include<cstdio>
#include<set>
#include<queue>
#include<vector>
using namespace std;

const int MAXN = 10000+1;
int n;
int father[MAXN];
vector<int> G[MAXN];
int deepest = 0;
set<int> ans;
bool vis[MAXN]={false};
int find(int i){
	int a = i;
	while(a!=father[a]){
		a=father[a];
	}
	while(i!=a){
		int temp = father[i];
		father[i] = a;
		i = temp;
	}
	return a;
}
void dfs(int root,int i,int deep){
	vis[i]=true;
	bool isLeaf = true;
	for(int num:G[i]){
		if(vis[num]==false){
			isLeaf=false;
			dfs(root,num,deep+1);
		}
	}
	if(isLeaf){
		if(deep>deepest){
			deepest = deep;
			ans.clear();
			ans.insert(root);
		}else if(deep==deepest){
			ans.insert(root);
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i<=n;i++){
		father[i]=i;
	}
	for(int i = 0;i<n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		int f1 = find(u);
		int f2 = find(v);
		if(f1!=f2){
			father[f1]=f2;
		}
	}
	int cnt = 0;
	for(int i =1;i<=n;i++){
		if(father[i]==i) cnt++;
	}
	if(cnt!=1){
		printf("Error: %d components\n",cnt);
		return 0;
	}
	for(int i = 1;i<=n;i++){
		fill(vis+1,vis+1+n,false);
		dfs(i,i,1);
	}
	for(int i:ans){
		printf("%d\n",i);
	}
	return 0;
}

 对于测试点3超时了

分析

void dfs(int root,int i,int deep){
	bool isLeaf = true;
	for(int num:G[i]){
		if(vis[num]==false){
			isLeaf=false;
			vis[num]=true;
			dfs(root,num,deep+1);
		}
	}
	if(isLeaf){
		if(deep>deepest){
			deepest = deep;
			ans.clear();
			ans.insert(root);
		}else if(deep==deepest){
			ans.insert(root);
		}
	}
}

全局变量set<int> ans用来存储最大深度的根节点,全局变量int deepest用来存储目前找到的最大深度

问题出在dfs这一步,我的想法是分别以每个顶点作为root,dfs遍历整个树.

当到达叶子节点时判断深度是否大于目前已知的最大深度,

如果叶子节点深度大于目前找到的最大深度,则清空ans并将当前root加入到ans中,

如果叶子节点深度等于目前找到的最大深度,则将root添加到ans中.

而这个判断并更新ans的过程需要花费大量的时间,以至于当节点数很大时,该程序会超时.

解决办法是以每个定点作为root并dfs遍历整棵树的时候,只更新树的最大深度,若最后得到树的最大深度大于等于全局最大深度,再更新ans.

修改后的代码如下:

代码v2

对dfs的修改:

void dfs(int i,int deep,int& cur_deepest){
	if(deep>cur_deepest){
		cur_deepest = deep;
	}
	vis[i]=true;
	for(int num:G[i]){
		if(vis[num]==false){
			dfs(num,deep+1,cur_deepest);
		}
	}
}

遍历顶点部分代码的修改:

	for(int i = 1;i<=n;i++){
		fill(vis+1,vis+1+n,false);
		int cur_deepest = 0;
		dfs(i,1,cur_deepest);
		if(cur_deepest>deepest){
			deepest = cur_deepest;
			ans.clear();
			ans.insert(i);
		}else if(cur_deepest==deepest){
			ans.insert(i);
		}
	}

成功通过了,但是可以看到测试点3耗时非常长.

代码v3(参考大神代码,2次dfs)

#include<cstdio>
#include<set>
#include<queue>
#include<vector>
using namespace std;

const int MAXN = 10000+1;
int n;
int father[MAXN];
vector<int> G[MAXN];
int deepest = 0;
set<int> ans;
int d[MAXN]={0};
bool vis[MAXN]={false};

int find(int i){
	int a = i;
	while(a!=father[a]){
		a=father[a];
	}
	while(i!=a){
		int temp = father[i];
		father[i] = a;
		i = temp;
	}
	return a;
}
void dfs(int i,int deep,int& cur_deepest){
	if(deep>cur_deepest){
		cur_deepest = deep;
	}
	d[i]=deep;
	vis[i]=true;
	for(int num:G[i]){
		if(vis[num]==false){
			dfs(num,deep+1,cur_deepest);
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i = 1;i<=n;i++){
		father[i]=i;
	}
	for(int i = 0;i<n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		int f1 = find(u);
		int f2 = find(v);
		if(f1!=f2){
			father[f1]=f2;
		}
	}
	int cnt = 0;
	for(int i =1;i<=n;i++){
		if(father[i]==i) cnt++;
	}
	if(cnt!=1){
		printf("Error: %d components\n",cnt);
		return 0;
	}
	int cur_deep = 0;
	dfs(1,1,cur_deep);
	for(int i = 1;i<=n;i++){
		if(d[i]==cur_deep){
			ans.insert(i);
		}
	}
	fill(vis+1,vis+1+n,false);
	cur_deep=0;
	dfs(*(ans.begin()),1,cur_deep);
	for(int i = 1;i<=n;i++){
		if(d[i]==cur_deep){
			ans.insert(i);
		}
	}	
	for(int i:ans){
		printf("%d\n",i);
	}
	return 0;
}

 参考文献:

1.PAT 1021 Deepest Root (25分) 从测试点3超时到满分再到代码优化 - 无代码,非程序 - 博客园

2.1021. Deepest Root (25) DFS & (a little idea )_Uncle_Sugar的博客-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值