找环

找环

1.算法分析

1.1 判断是否存在环

如果是无向图,那么可以使用:

  1. 并查集判断是否存在环(并查集不仅能够判断是否存在环,还能判断是否存在奇环)
  2. 使用dfs打标记的方法判断是否存在环。
  3. tarjan缩点的方式判断是否存在双连通分量,如果存在必然存在环
  4. spfa判断是否存在环

如果是有向图,那么可以使用:

  1. dfs打标记的方法判断是否存在环
  2. tarjan缩点的方式判断是否存在强连通分量,如果存在必然存在环
  3. spfa判断是否存在环

特殊环判定:

  1. 奇环:并查集判定、染色法判断(是否为二分图)

  2. 正环、负环:spfa判断、tarjan缩点判定

1.2 找出所有环上的所有点

无向图:

  1. dfs打标记的方式找环,找到的是否记录pre数组,表示当前点的前一个点是哪个
  2. tarjan缩点后双连通分量就是多个环的并
  3. spfa记录pre数组打印环

有向图:

  1. dfs打标记的方法找环,使用pre数组记录
  2. tarjan缩点后强连通分量就是多个环的并
  3. spfa记录pre数组打印环

注意:

dfs和spfa使用pre数组找到的环,都是一个简单环

1.3 类拓扑排序找出与度数有关的点集->退化到度数为2的环

可以使用类似拓扑排序的方法找到一个点集,使得点集在任意一个点的度都大于等于k。如果k=2,那么就是一个环。

1.4 找到以s为源点的最小环

对spfa算法进行修改可以完成这个操作: O ( k m ) O(km) O(km)

1.5 floyd找到有向图/无向图的最小环

floyd+dp可以找到有向图/无向图的最小环: O ( n 3 ) O(n^3) O(n3)

2.模板

2.1 判断是否存在环

具体见 并查集、强连通分量、双连通分量、匹配问题

2.2 找出所有环上的所有点

维护pre数组,倒着找就行

2.3 类拓扑排序找出与度数有关的点集->退化到度数为2的环

见3.3

2.4 找到以s为源点的最小环

枚举版本

// spfa求最短路且找到以s为起点回到s的最小环
// 最后找到的最小环大小为dis[s]
void spfa(int s) {
   
    queue<int> q;  // 建立新队列
    for (int i = 1; i <= n; ++i) {
   
        st[i] = 0;
        if (i == s) dis[i] = INF;  // 初始化要求最小环的点
        else {
   
            dis[i] = mp[s][i];
            st[i] = 1;
            q.push(i);
        }
    }
    
    while (q.size()) {
   
        auto t = q.front();  
        q.pop();  // 队首元素出队
        st[t] = 0;  // 记录出队

        for (int i = 1; i <= n; ++i) {
   
            if (dis[i] > dis[t] + mp[t][i]) {
   
                dis[i] = dis[t] + mp[t][i];
                if (!st[i]) {
     // 如果j点不在队列里
                    q.push(i);  // 入队
                    st[i] = 1;  // 记录
                }
            }
        }
    }
}

链式前向星版本

int spfa(int s) {
   
    queue<int> q;  // 建立新队列
    for (int i = 1; i <= n; ++i) {
   
        st[i] = 0;
        if (i == s) dis[i] = INF;  // 初始化要求最小环的点
        else {
   
            if (mp[s][i] == 1) dis[i] = 1, pre[i] = s;  // 如果s和i点有边的话,这里记录pre数组还能够找到这个最小的环是什么
            else dis[i] = INF;  // 如果没有边
            st[i] = 1;
            q.push(i);
        }
    }

    while (q.size()) {
   
        auto t = q.front();
        q.pop();    // 队首元素出队
        st[t] = 0;  // 记录出队

        for (int i = h[t]; ~i; i = ne[i]) {
   
            int j = e[i];
            if (dis[j] > dis[t] + 1) {
   
                dis[j] = dis[t] + 1;
                pre[j] = t;
                if (!st[j]) {
   
                    q.push(j);
                    st[j] = 1;
                }
            }
        }
    }
    return dis[s];
}

2.5 floyd找最小环

3.典型例题

3.1 类拓扑排序找出与度数有关的点集->退化到度数为2的环

CF 1440D.Graph Subset Problem

题意: 给定一张n个点m条边的图,问是否能够找到一个点集,点集中每个点的度都大于等于k。问是否能够找到一个团,团中每个点的度都为k-1。如果能够找到点集或者团,那么输出其中一个即可,否则输出-1

题解: 找点集或者团可以使用类似拓扑排序的方法,每次把度数小于k的点放入队列,然后每次删除其连接点,度数减一,如果度数晓宇k,那么再次放入队列。在做这一过程的时候判断是否存在团,判断方法为暴力枚举,如果当前这个点的度为k-1,那么只需要将其所有的邻接点放入团内,然后枚举任意两个团内的点,二分判断a点是否为与b点的邻接点。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值