ACWing 4493 -- 思维题&&并查集&&dfs

题目描述

环形连通分量

思路

对于一个无向图中的 简单环(环中边的数量等于点的数量),有一个很强的性质:每个点的度数等于 \(2\)
那么我们只需要先找出所有的连通块,然后判断每个连通块中的每条边的度数是否位 \(2\) 就可以判断该点所在连通块是不是一个简单环了。

找连通块

求连通块问题是一个图论中的经典问题,我们有三种方法:

  1. 并查集
  2. \(dfs\)
  3. \(bfs\)

其中并查集最简单,代码最短,因此对于求连通块问题优先考虑并查集

代码1:并查集

依据性质:判断每个点的度数是否为2

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 200010;

int n, m;
int pre[N];
bool st[N]; // st[i]=false -> 连通块 i 是简单环,= true -> 不是简单环
int d[N];

int find(int x)
{
    if(pre[x] == x) return x;
    return pre[x] = find(pre[x]);
}

int main()
{
    for(int i = 0; i < N; i ++ )    pre[i] = i;
    cin >> n >> m;
    
    // first. 找到所有连通块,计算每个点的度数
    while(m -- )
    {
        int a, b;
        cin >> a >> b;
        pre[find(a)] = find(b);       // a<-->b,合并到同一个连通块
        d[a] ++ ;       // 度数 ++ 
        d[b] ++ ;       // 度数 ++
    }
    
    // 不要傻乎乎的去真的遍历“每个连通块中”的所有点
    // 那样的话我们还要找到任意连通块中的点集合
    // 只需要直接遍历所有点来判断每个点的度数是否为2就行
    for(int i = 1; i <= n; i ++ )
    {
        if(d[i] != 2)
        {
            st[find(i)] = true;
        }
    }
    
    int res = 0;
    for(int i = 1; i <= n; i ++ )
    {
        if(pre[i] == i && !st[i])  res ++ ;
    }
    cout << res << endl;
    
    return 0;
}

代码2:dfs:

判断每个点是否连了两条边
这其实是对性质:简单环中每个点的度数为 \(2\) 的另外一种表达形式。
每个点的度数为 \(2\) 就等价于每个点连了 \(2\) 条边。
我们这里通过 \(dfs\) 来判断

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010, M = N << 1;

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

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

bool dfs(int u)
{
    // 标记为已经遍历过,需要放在 dfs[e[i]] 的前面
    st[u] = true;
    
    bool has_res = true;
    int cnt = 0;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        cnt ++ ;
        if(!st[e[i]] && !dfs(e[i]))   has_res = false; // 需要继续dfs下去直到遍历完整个连通块
    }
    return has_res && (cnt == 2);
}

int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while (m -- )
    {
        int a, b;
        cin >> a >> b;
        add(a, b);  add(b, a);
    }
    
    // 判断每个点是否连了两条边
    int res = 0;
    for(int i = 1; i <= n; i ++ )
    {
        if(st[i])   continue;
        res += dfs(i);
    }
    
    cout << res << endl;
    return 0;
}
posted @ 2022-10-25 22:14  光風霽月  阅读(15)  评论(0编辑  收藏  举报