思路:
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博客