PAT_甲级_1021 Deepest Root (25分) (C++)【DFS/连通分量/段错误/超时 #简单清晰!】

目录

1,题目描述

题目大意

输入

输出

2,思路

思路一

思路二

算法

段错误

3,代码

思路一

思路二


1,题目描述

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

 

题目大意

一棵树以不同的节点作为根节点时,树可能会有不同的高度,使树的高度达到最大值时的节点为Deepest Root。

输入

第一行:节点数目N(<=10^4,编号1-N)

N-1行:描述树中每条边连接的两个节点

输出

若为非连通图,输出Error: k components ,k即连通分量数目

否则,按序号从小到大,输出所有的Deepest Root;

2,思路

思路一

野蛮解法。先判断是否为连通图,若不是连通图,则对每一个节点调用一次dfs,保留并更新Deepest Root。

结果显示这种算法会有一个测试点超时。(还有一个段错误,可能是我自己代码的问题。。。)

 

思路二

参考柳神的解题思路。

两次dfs。第一次dfs保留所有的最高高度的节点,从中选择一个节点,进行第二次dfs,同样保留所有的最高高度节点。两个节点集合的并集,就是所求内容。 (很神奇!!!)

 

算法

  • 第一次dfs算法过程中,用set<int> depth记录所有具有最高高度的节点;
  • 判断是否还有节点未被访问(有多个联通分量),是的话只需要继续dfs并输出连通分量个数即可,否则继续;
  • 将set<int> depth赋值给set<int> ans,并从ans中取一个元素(第一个就行)再次进行dfs,用set<int> depth记录所有具有最高高度的节点;
  • 将ans与depth中的内容合并(求并集)即可;

 

段错误

  • 若出现段错误,请检查在接收边的数据时,你的上限是<n还是<n-1。n的话,问题就在这里了。

 

3,代码

思路一

也把思路一的代码贴出来,有兴趣的同学可以看一下。(有一个测试点超时,还有一个段错误)

#include<iostream>
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;

bool visited[10001];
bool graph[10001][10001];
vector<int> depth;
int temDeep = 0, deepest = 0, n = 0, num = 1;

void dfs(int start, int deep){
    visited[start] = true;
    for(int i = 1; i <= n; i++){
        if(graph[start][i] == true && visited[i] == false) dfs(i, deep+1);
    }
    if(deep > temDeep) temDeep = deep;
}

int main(){
//#ifdef ONLINE_JUDGE
//#else
//    freopen("1.txt", "r", stdin);
//#endif


    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        int a, b;
        scanf("%d%d", &a, &b);
        graph[a][b] = graph[b][a] = true;
    }

    for(int i = 1; i <= n; i++){
        fill(visited, visited + 10001, false);
        temDeep = 0;
        dfs(i, 0);

        for(int j = 1; j <= n; j++){
            if(visited[j] == false){
                num += 1;
                dfs(j, 0);
            }
        }
        if(num > 1){
            printf("Error: %d components", num);
            return 0;
        }

        if(temDeep > deepest){
            deepest = temDeep;
            depth.clear();
            depth.push_back(i);
        }else if(temDeep == deepest){
            depth.push_back(i);
        }
    }


    for(int i = 0; i < depth.size(); i++){
        printf("%d\n", depth[i]);
    }

    return 0;
}

思路二

绝对清晰易懂!

#include<iostream>
#include<set>
#include<algorithm>
using namespace std;

bool visited[10001];
bool graph[10001][10001];
set<int> ans, depth;                        //ans最终结果 depth暂时存放具有最高高度的节点
int temDeep = 0, n = 0, num = 1;            //tempDeep临时最大高度 n节点数目 num连通分量数

void dfs(int start, int deep){
    visited[start] = true;
    for(int i = 1; i <= n; i++){
        if(graph[start][i] == true && visited[i] == false) dfs(i, deep+1);
    }
    if(deep > temDeep){
        temDeep = deep;
        depth.clear();
        depth.insert(start);
    }else if(deep == temDeep){
        depth.insert(start);
    }
}

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n-1; i++){           //注意是n-1(n个节点 n-1条边)!!!
        int a, b;
        scanf("%d%d", &a, &b);
        graph[a][b] = graph[b][a] = true;
    }

    fill(visited, visited + 10001, false);
    dfs(1, 1);
    for(int j = 1; j <= n; j++){
        if(visited[j] == false){
            num += 1;
            dfs(j, 1);
        }
    }
    if(num > 1){                            //判断连通分量数目
        printf("Error: %d components", num);
        return 0;
    }

    ans = depth;                            //第一次dfs中具有相同最高高度的节点 存放至答案set中
    depth.clear();
    fill(visited, visited + 10001, false);
    temDeep = 0;
    dfs(*ans.begin(), 1);                   //在第一次dfs中具有相同最高高度的节点中 选择一个进行第二次dfs

    for(auto it = depth.begin(); it != depth.end(); it++){// it != depth.end()
        ans.insert(*it);                    //两个集合求并集(由于是set 直接插入即可)
    }
    for(auto it = ans.begin(); it != ans.end(); it++){
        printf("%d\n", *it);
    }

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值