tarjan求割点和桥(割边)模板

Posted hesorchen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tarjan求割点和桥(割边)模板相关的知识,希望对你有一定的参考价值。

tanjan算法相关概念

为了与有向图尽可能保持一致,我们将无向图的一条无向边拆分成两条单向边。两条边互为反向边

从图中一点作为起点,进行DFS搜索遍历图,这样会得到一棵树,我们称之为DFS搜索树,该树中的每一条边都来自原图,我们将这些边称为树边,其他图中的边称为非树边

DFN数组:记录DFS序,也即时间戳、搜索顺序。
LOW数组:记录每个点经过一条非树边能回到的所有结点的最小DFN值

割点

割点集合:在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。

求解

设一个点 u u u,其父亲结点为 f u fu fu,如果有 L O W [ u ] > = D F N [ f u ] LOW[u] >= DFN[fu] LOW[u]>=DFN[fu],那么说明,点 u u u仅可以经过点 f u fu fu抵达,此时,如果删除点 f u fu fu,那么 u u u就和 f u fu fu所在的图不再连通, f u fu fu就是一个割点。

对于根结点而言是一种特殊情况,因为不可能有点的 L O W [ u ] LOW[u] LOW[u]会小于 D F N [ f u ] DFN[fu] DFN[fu]。根结点为割点当且仅当有 2 2 2个以上子节点时,只需特判即可。

tarjan求割点代码

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

const int N = 1e5 + 5;
vector<int> edge[N];
int dfn[N], root[N];
int ct;
vector<int> cut_points;

void tarjan(int u, bool isroot)
{
    int tot = 0;
    root[u] = dfn[u] = ++ct;
    for (auto v : edge[u])
    {
        if (!dfn[v])
        {
            tarjan(v, 0);
            root[u] = min(root[u], root[v]);
            tot += root[v] >= dfn[u];
        }
        else
            root[u] = min(root[u], dfn[v]);
    }
    if ((isroot && tot >= 2) || (!isroot && tot >= 1))
        cut_points.emplace_back(u);
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        edge[u].emplace_back(v);
        edge[v].emplace_back(u);
    }
    tarjan(1, 1);
    cout << "cut_points: ";
    for (auto it : cut_points)
        cout << it << ' ';
    return 0;
}
/*
5 5
1 2
2 3
3 4
2 4
4 5
*/

割边

割边:假设有连通图G,e是其中一条边,如果G-e是不连通的,则边e是图G的一条割边。此情形下,G-e必包含两个连通分支。

和割点对应,删除一条割边后,连通分量加一。

求解

对LOW数组的定义略加修改:记录每个点经过一条非树边能回到的所有结点的最小DFN值,并且该非树边不可以是某条树边的反向边

tarjan求割边代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
vector<int> edge[N];
int dfn[N], root[N], fa[N], ct;
struct node
{
    int u, v;
};
vector<node> cut_edge;
void tarjan(int u)
{
    int tot = 0;
    root[u] = dfn[u] = ++ct;
    for (auto v : edge[u])
    {
        if (!dfn[v])
        {
            fa[v] = u;
            tarjan(v);
            root[u] = min(root[u], root[v]);
            if (root[v] > dfn[u])
                cut_edge.emplace_back(node{u, v});
        }
        else if (v != fa[u])
            root[u] = min(root[u], dfn[v]);
    }
}
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        edge[u].emplace_back(v);
        edge[v].emplace_back(u);
    }
    tarjan(1);
    cout << "cut_edge:\\n";
    for (auto it : cut_edge)
        cout << it.u << "--" << it.v << '\\n';
    return 0;
}
/*
5 5
1 2
2 3
3 4
2 4
4 5
*/

参考资料

  1. 算法学习笔记(70): 割点和桥

  2. Tarjan无向图的割点和桥(割边)全网详解&算法笔记&通俗易懂

以上是关于tarjan求割点和桥(割边)模板的主要内容,如果未能解决你的问题,请参考以下文章

割点和桥

tarjan求割边割点

图的联通-割点和桥

tarjan求割点割边的思考

tarjan求割点割边的思考

求割点 割边 Tarjan