无向图判环+判连通性

理论链接

例题
HDU1272 小希的迷宫
AcWing 4290. 小希的迷宫

题目要求:判断是不是连通无环的图

总结
1、必须使用scanf!使用cin直接TLE!
2、无向图判环,应尽量采用并查集(判断连通性)+公式法(判环)
3、

方法1:数学公式法判环+并查集判连通性

判断一个图是否为连通图且没有环其实就一个条件,点数-1=边数,但是需要特判(0,0)
此方法可以通过AcWing+HDU

方法2:并查集

  • 并查集判环,不用真的建图
  • 因为不一定每个点都用上,所以需要记录本轮使用了哪些节点
  • 检查是否有环:
    判断输入边的两个点,如果它们已经在同一个家族内,说明有环
  • 无环情况下,还需要检查是不是连通图。
    这时,需要判断家族数量是不是1
    此方法可以通过AcWing+HDU

方法3:DFS标记白灰黑

  • DFS判环,需要使用邻接表建图

  • 因为不一定每个点都用上,所以需要记录本轮使用了哪些节点

  • 检查是否有环:
    (1)枚举每个节点,找到一个使用过的节点进去开始深搜
    (2)需要单开一个状态数组vis,来记录哪些节点深搜过了

  • 在无环情况下,还需要判断是不是连通
    枚举每个节点,如果出现过,但深搜没有搜索过,就意味着不连通
    此方法HDU可AC,AcWing会SE,原因可能是因为内存过大。

并查集+公式

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;

bool st[N];
bool flag;
int edges, points;
//最简并查集
int p[N];
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]); //路径压缩
    return p[x];
}
//合并集合
bool join(int a, int b) {
    if (find(a) == find(b)) return false;
    p[find(a)] = find(b);
    return true;
}

//初始化并查集
void init() {
    for (int i = 0; i < N; i++) p[i] = i;
    //初始化状态数组
    memset(st, false, sizeof(st));
    flag = true;
    edges = points = 0;
}

int main() {
    int a, b;
    //初始化
    init();
    while (~scanf("%d%d", &a, &b), ~a && ~b) {
        if (a && b) {
            edges++; //增加一条边
            if (!st[a]) {
                st[a] = true;
                points++;
            }
            if (!st[b]) {
                st[b] = true;
                points++;
            }
            if (!join(a, b)) flag = false;
        } else {
            //这个是应付AcWing的黑心数据 0 0 -1 -1而加的特判
            //如果连通,并且,无环
            flag && (edges == points - 1 || edges == 0) ? puts("Yes") : puts("No");
            init();
        }
    }
    return 0;
}

并查集+枚举

#include <bits/stdc++.h>
using namespace std;
const int N = 100500;

int n, m;         // n个顶点,m条边
bool flag = true; //默认是合格的,如果有环是false,不连通也是false
//每组数据,是否符合要求:1、任意两点间道路唯一,2、需要是连通的
bool st[N]; //节点是不是出现过

//最简并查集
int p[N];
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]); //路径压缩
    return p[x];
}
//合并集合
bool join(int a, int b) {
    if (find(a) == find(b)) return false;
    p[find(a)] = find(b);
    return true;
}

// 初始化各种状态数组和标识数据
void init() {
    flag = true;
    //初始化并查集
    for (int i = 0; i < N; i++) p[i] = i;
    //清空状态数组
    memset(st, 0, sizeof st);
}

int main() {
    int a, b;
    //初始化
    init();
    while (~scanf("%d%d", &a, &b), ~a && ~b) {
        if (a && b) {               //一组数据输入进行中
            st[a] = st[b] = true;   //标识a,b两个点走过了
            if (!join(a, b))        //如果两个点在一个集合中,说明有重复路线出现~
                flag = false;        //标记已经不合法
        } else {
            //单组数据输入结束
            //如果没有检查到环,才有资格检查是不是连通
            if (flag) {
                int cnt = 0;
                for (int i = 1; i < N; i++) {
                    if (st[i] && find(i) == i) cnt++; //判断根节点cnt数目
                    if (cnt > 1) {
                        flag = false;
                        break;
                    }
                }
            }
            //检查通过
            flag ? puts("Yes") : puts("No");

            //清空准备下一次输入
            init();
        }
    }
    return 0;
}

dfs

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = N << 1;
/*
7e5+10
=700000*4/1024/1024=2.67MB
这内存也不大啊,为啥会SE呢?不是说64MB的上限吗?
被卡的好难受,这是逼我去用并查集+公式解题啊!
*/
//邻接表
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool st[N];  //标识某个节点号是不是使用过
bool vis[N]; //是不是在dfs过程中搜索过
int flag;    // true:满足两个条件 false:不满足两个条件 (1)是不是有环,要求无环 (2)是不是连通,要求连通

// dfs无向图判环
void dfs(int u, int fa) {               //从u节点开始搜索,它的父节点是fa,输入fa的意义在于不能走回头路,防止死循环
    vis[u] = true;                      //标识u已经搜索过了
    for (int i = h[u]; ~i; i = ne[i]) { //枚举u的每一条出边
        int j = e[i];
        if (j == fa) continue; //放过来源节点
        if (vis[j]) {          //如果j被搜索过,说明有环
            flag = false;      //标识有环
            return;
        }
        dfs(j, u); //继续搜索j节点,它的父节点是u
    }
}
//每组输入的清空与初始化
void init() {
    memset(st, 0, sizeof(st));
    memset(vis, 0, sizeof(vis));
    memset(h, -1, sizeof h);
    idx = 0;
}

int main() {
    int a, b;
    init();
    while (scanf("%d%d", &a, &b) && ~a && ~b) {
        if (a && b) {                   //如果输入的a,b是两个非零数字
            st[a] = st[b] = true;       //标识a,b已使用
            add(a, b), add(b, a);       //添加两条无向边
        } else {                        //如果输入的是两个0,描述是一组数据输入结束
            flag = true;                //默认是无环+连通图
            // 1、检查是不是无环
            for (int i = 1; i < N; i++) //枚举每个节点
                if (st[i]) {            //如果i节点使用过
                    //从找到的第一个使用过的节点开始进行dfs扫描
                    dfs(i, i);
                    break;
                }
            // 2、检查连通性
            if (flag) { //扫描完的结果:1、找到了环,2、没有找到环
                //下面将现次枚举每个使用过的点,查看它是不是在dfs过程中被扫描到了,如果存在使过未扫到的点,则说明不连通
                for (int i = 1; i < N; i++)
                    if (st[i] && !vis[i]) { //如果存在出现过,但dfs没有扫描到的点,说明不连通
                        flag = false;
                        break;
                    }
            }
            flag ? puts("Yes") : puts("No"); //两个条件都满足,输出Yes,否则输出No
            init();                          //因为要输入下一组数据,需要清空状态数组
        }
    }
    return 0;
}
posted @   糖豆爸爸  阅读(205)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2018-05-16 Python3.6安装OpenCV
2018-05-16 CAS单点登录安装笔记
2018-05-16 pycharm+PyQt5+python最新开发环境配置
Live2D
点击右上角即可分享
微信分享提示