Two Spanning Trees(DFS树,BFS树)

题意

给定一个\(n\)个点,\(m\)条边的无向连通图\(G\),并且\(G\)是简单图。找到两个满足如下条件的生成树\(T_1\)\(T_2\)

  • 如果我们把\(T_1\)看作以\(1\)为根节点的有向图,对于任意一条不包含在\(T_1\)中的边(非树边),两个点中一个点是另一个点在\(T_1\)中的祖先。

  • 如果我们把\(T_2\)看作以\(1\)为根节点的有向图,对于任意一条不包含在\(T_2\)中的边(非树边),不能出现两个点中一个是另一个在\(T_2\)中的祖先的情况。

数据范围

\(2 \leq n \leq 2 \times 10^5\)
\(1 \leq m \leq 2 \times 10^5\)

思路

这道题是DFS树和BFS树的模板题。

首先介绍DFS树。对一个无向连通图\(G\)进行深度优先遍历,得到的树为DFS树。其中,遍历到的边为树边,回溯的边为回边(非树边)。DFS树的一个重要性质是,图的回边连接的都是一个顶点和它在DFS树中的子孙节点。根据这条性质,我们发现DFS树满足\(T_1\)的要求。

然后介绍BFS树。对一个无向连通图\(G\)进行宽度优先遍历,得到的树为BFS树。其中,遍历到的边为树边,回溯的边为回边(非树边)。BFS树的一个重要性质是,图的回边连接的两点一定在BFS树的同一层或者相邻的两层。并且,如果处于相邻两层,其中一个点不可能是另一个点的父节点(因为如果是父节点,那么就是树边了)。根据这条性质,我们发现BFS树满足\(T_2\)的要求。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 200010, M = 2 * N;

int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int u)
{
    st[u] = true;
    for(int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];
        if(!st[j]) {
            printf("%d %d\n", u, j);
            st[j] = true;
            dfs(j);
        }
    }
}

void bfs(int u)
{
    queue<int> que;
    que.push(u);
    st[u] = true;
    while(que.size()) {
        int t = que.front();
        que.pop();
        for(int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if(!st[j]) {
                st[j] = true;
                que.push(j);
                printf("%d %d\n", t, j);
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    for(int i = 0; i < m; i ++) {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    dfs(1);
    memset(st, 0, sizeof st);
    bfs(1);
    return 0;
}
posted @ 2022-05-17 14:49  pbc的成长之路  阅读(239)  评论(0编辑  收藏  举报