zoukankan      html  css  js  c++  java
  • PTA-7-6 列出连通集(并查集+DFS+BFS)

    本题考查点:

    • 图的并查集
    • 图的 BFS 和 DFS

    题目描述:

    给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。

    输入格式:

    输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。

    输出格式:

    按照"{ v1 v2 ... v**k }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。

    输入样例:

    8 6
    0 7
    0 1
    2 0
    4 1
    2 4
    3 5
    

    输出样例:

    { 0 1 4 2 7 }
    { 3 5 }
    { 6 }
    { 0 1 2 7 4 }
    { 3 5 }
    { 6 }
    

    本题其实并不复杂,就是常规的图的并查集与DFS和BFS的结合,有关图的并查集和可以点击回顾。

    我们需要熟悉图的 BFS 和 DFS 思想,题目经常就是图的遍历加上某个条件的阈值设定。

    我们在进行 DFS 和 BFS 实现的时候,需要设定一个 vis[] 数组来保存该点是否已经访问过。

    而我们关于图的DFS 有常规思路如下:

    void DFS(u) {
      vis[u] = true;	// 设置为已访问
      for(从u出发能达到的所有顶点v)	// 枚举从u出发可以到达的所有顶点
        	if vis[v] == false	// 没有被访问
            	DFS(v)	// 递归访问
    }
    
    void DFSTravel(G) {
      for(G所有顶点u)
        if vis[u] == false
          DFS(u)
    }
    

    而同样,关于图的 BFS 有常规思路如下:

    void BFS(int u) {
      queue q;
      q.push(u);
      inq[u] = true;	// 设置 u 已经入队
      while(!q.empty()) {
        取出队首元素进行访问
        for(从u出发可到达所有顶点v)
          	if(inq[v] == false)
              将 v 入队
              inq[v] = true
      }
    }
    
    void BFSTravel() {
      for(G所有顶点u) {
        if(inq[u] == false)
          	BFS(u)
      }
    }
    

    而并查集我们可以不管左右树的高度直接实现并查集。

    完整代码如下:

    /*
        Author: Veeupup
        列出连通集
    
        给定一个有 N 个顶点和 E 条边的无向图,请用 DFS 和 BFS 分别列出其所有的连通集
        假设顶点从 0~N-1 编号,进行搜索时,我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点
    
        并查集 + DFS + BFS
    
        注意这里的 DFS 和 BFS 都要设置一个 访问数组 vis[] 来记录是否递归访问过或者是否入过队
    
     */
    #include <iostream>
    #include <cstdio>
    #include <cstdint>
    #include <cstring>
    #include <vector>
    #include <queue>
    using namespace std;
    
    const int maxn = 12;
    
    int n, e;                   // 顶点数目和边数
    int G[maxn][maxn];          // 保存图
    bool visit[maxn] = {false}; // 记录是否访问过
    
    int father[maxn]; // 记录父亲节点
    
    void Initial()
    {
        fill(visit, visit + maxn, false);
        for (int i = 0; i < maxn; i++)
        { // 初始化父亲节点
            father[i] = i;
        }
    }
    
    // 查
    int findFather(int x)
    {
        while (x != father[x])
        {
            x = father[x];
        }
        return x;
    }
    
    // 并
    void Union(int x, int y)
    {
        x = findFather(x);
        y = findFather(y);
        if (x != y)
        {
            father[x] = y;
        }
    }
    
    // 深度优先遍历
    // 我们只需要从第一个结点开始
    void DFS(int nowVisit, vector<int> &sets)
    {
        for (int i = 0; i < n; i++)
        {
            if (G[nowVisit][i] != 0 && findFather(nowVisit) != findFather(i))
            {   // 如果二者连通 且 不在同一个集合中
                // 二者不在同一个集合中
                Union(nowVisit, i); // 合并到一个集合中
                sets.push_back(i);  // 加入到当前集合中
                if (visit[i] == false)
                { // 如果没有被DFS过,那么就深度优先遍历
                    visit[i] = true;
                    DFS(i, sets);
                }
            }
        }
    }
    
    void DFSTravel()
    {
        Initial();
        for (int i = 0; i < n; i++)
        {
            if (visit[i] == false)
            { // 如果没有被访问,那么深度优先遍历
                vector<int> sets;
                sets.push_back(i);
                visit[i] = true;
                DFS(i, sets);
                printf("{");
                for (int i = 0; i < sets.size(); i++)
                {
                    printf(" %d", sets[i]);
                }
                printf(" }
    ");
            }
        }
    }
    
    void BFS(int nowVisit, vector<int>& sets) {
        queue<int> myQ;
        myQ.push(nowVisit);
        visit[nowVisit] = true; // 设置已经访问过
        while (!myQ.empty())
        {
            int topId = myQ.front();
            myQ.pop();
            sets.push_back(topId);
            Union(nowVisit, topId); // 当前点合并到集合中
            for (int i = 0; i <= n; i++)
            {   // 当前点所连接的到的点不是在同一个集合中 , 没有入过队
                if(G[topId][i] != 0 && findFather(nowVisit) != findFather(i) && visit[i] == false) {
                    myQ.push(i);    // 加入到集合中
                    visit[i] = true;    // 已经入过队
                }
            }
        }
    }
    
    void BFSTravel() {
        Initial();
        for (int i = 0; i < n; i++)
        {
            if(visit[i] == false) {
                vector<int> sets;
                BFS(i, sets);
                printf("{");
                for (int i = 0; i < sets.size(); i++)
                {
                    printf(" %d", sets[i]);
                }
                printf(" }
    ");
            }
        }
        
    }
    
    int main()
    {
        freopen("data.txt", "r", stdin);
        memset(G, 0, sizeof(sizeof(int) * maxn * maxn));
        scanf("%d%d", &n, &e);
        int v1, v2; // 两条边的顶点
        for (int i = 0; i < e; i++)
        { // 读取边
            scanf("%d%d", &v1, &v2);
            G[v1][v2] = 1;
            G[v2][v1] = 1;
        }
        DFSTravel();
        BFSTravel();
        return 0;
    }
    
  • 相关阅读:
    Mybatis的基本步骤
    Log笔记
    通过vue-cli脚手架创建VUE项目
    c语言字符相关函数
    嵌入式:电路设计之三极管基础电路设计
    晶体三极管进行双路控制驱动电路设计
    三极管实用
    再谈一维数组、二维数组与指针、数组指针
    光电耦合器工作原理详细解说
    任务控制块
  • 原文地址:https://www.cnblogs.com/veeupup/p/12592806.html
Copyright © 2011-2022 走看看