【PAT算法之路】 -- 并查集 1021 Deepest Root (25 分) C++解法

好久不见. 提交于 2019-11-26 16:13:16

【PAT算法之路】 – 专栏总揽

并查集在最近的PAT中也较常出现,并查集本身的代码非常优雅、简洁,如果第一次接触一定会惊叹它的简洁的。本身代码大概20行。在理解的基础上直接背下就行。我们选一个难一点的题来举例吧!

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

给出n个结点(1~n)之间的n条边,问是否能构成一棵树,如果不能构成则输出它有的连通分量个数,如果能构成一棵树,输出能构成最深的树的高度时,树的根结点。如果有多个,按照从小到大输出。

一开始我也是没有思路的 ╮(╯▽╰)╭,直到看到下面的算法:

先一遍遍历得到深度最大的所有点的集合,再从集合中随便找一个节点,从该点出发再做一遍遍历,得到另一个深度最大的所有点的集合,两个集合的并集即为结果。证明可以看《算法笔记》,我也没有看,看也看不懂╮(╯▽╰)╭

我用的并查集确定是否连通,用bfs遍历,容我先给出代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>

using namespace std;

vector<int> g[10010];
bool vis[10010];
int father[10010];

int findFather(int a){
    if(a != father[a])
        father[a] = findFather(father[a]);
    return father[a];
}

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

set<int> last_vec;
void bfs(int s){
    fill(vis,vis+10010,false);
    queue<int> q;
    q.push(s);
    vis[s] = true;
    while(!q.empty()){
        int len = q.size();
        set<int> vec;
        for(int i=0;i<len;i++){
            int now = q.front();
            vec.insert(now);
            q.pop();

            for(auto item:g[now])
                if(!vis[item]){
                    q.push(item);
                    vis[item] = true;
                }
        }
        last_vec = vec;
    }
}

int main() {
    for(int i=0;i<10010;i++){
        father[i] = i;
    }

    int n;
    cin >> n;
    for(int i=1;i<n;i++){
        int a,b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
        Union(a,b);
    }

    set<int> root;
    for(int i=1;i<=n;i++){
        root.insert(findFather(i));
    }

    if(root.size() > 1){
        printf("Error: %d components",root.size());
        return 0;
    }
    /////////////////////////////////////////
    bfs(1);
    set<int> res = last_vec;
    bfs(*(res.begin()));
    res.insert(last_vec.begin(),last_vec.end());

    for(auto item:res){
        cout << item << endl;
    }
    return 0;
}

因为这一篇主要讲并查集,所以我们看分割线上面的部分,根据并查集判断是否连通。

并查集的主要思想就是同一类的结点,它们的根结点一定相同,这里的根结点就是father[x] == x的结点。而对于一个集合中,谁当根结点并不重要,但是有且只有一个。

一、定义和初始化

首先记住int father[](生子当如孙仲谋 – 我是你xx),然后一定在读入数据前就初始化father,让每个元素当自己的father。即每一个结点初始化都是各管各的,没有交集。

二、定义findFather、Union

狠一点,把函数名都记下吧(选你记得住的),findFather是选择当前元素的父亲。Union是合并两个元素,让它们有共同的父亲。

findFather尤其用递归方式非常简洁,上面的方法还带路径压缩,就是比如:a的父亲b的父亲c的父亲为d,就直接变成了a的父亲为d。father[a] == d,这在你查询父亲时就直接完成了。

Union 就记住一句话:选个人当父亲,即比如:结点集合A的结点a,和结点集合B的结点b需要Union,那么它们各自集合的根节点选一个当根节点。

如果上面的话看晕了,先百度理解下并查集。

三、输入数据时完成设置父亲或者Union

根据题意完成,比如这道题是给的两个点,就直接Union就行,还一些题如1107需要设置一个数组,根据题意Union.

最后再提一下BFS,对于PAT的大部分遍历都可以用BFS,柳婼大神貌似更习惯用DFS,反正我太菜了,复杂的递归看起来头晕。而对于BFS,需要注意的是一定掌握下面这种写法,即一次while必须把上一次存的元素用完,这样有很多好处,比如:对于树的层次遍历可以得到树的层数,对于图的遍历,可以处理每一圈的元素,比如这道题。等等。。

 while(!q.empty()){
        int len = q.size();
        // 根据需要定义一些变量
        
        for(int i=0;i<len;i++){
            int now = q.front();
            q.pop();
			// 根据需要执行一些操作

			// 根据情况q.push
        }
    }

总结,作者水平有限,有哪里有问题都欢迎评论指出。如果对你有帮助欢迎点赞。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!