Tarjan算法与割点割边

目录

  • Tarjan算法与无向图的连通性

    • 1:基础概念
    • 2:Tarjan判断割点
    • 3:Tarjan判断割边

Tarjan算法与无向图的连通性

1:基础概念

在说Tarjan算法求解无向图的连通性之前,先来说几个概念:

<1. 时间戳:在图的深度优先遍历中,按照每一个结点第一次被访问到的时间顺序,依次给予N个结点1~N的整数边集,该标记就被计位“时间戳”,计做 \(dfn[x]\)。

<2. 搜索树:任选一个结点深度优先遍历,每个点只访问一次。产生递归的边构成的树为搜索树。

<3. \(subtree(x)\):搜索树中以x为根的子树。

<4. 追溯值:追溯值(\(low[x]\))定义为以下结点的时间戳的最小值,这些结点满足:

①:是\(subtree(x)\)的结点; ②:通过一条不在搜索树上的边,能够到达\(subtree(x)\)的结点

在了解概念之后,我们可以根据定义来计算\(low[x]\):

令\(low[x]=dfn[x]\);然后考虑每个从x的出边( x, y ),如果x是y的父节点,则\(low[x]=min(low[x],low[y])\);如果边不是搜索树上的边,则令\(low[x]=min(lwo[x],dfn[y])\); //ps:后面代码也会提到

2:Tarjan判断割点

判定定理:对于一个点 x ;如果x不为根节点,那么x是割点当且仅当搜索树上存在x 的一个子节点y,满足:
\[
dfn[x]<=low[y]
\]
如果x是根节点,那么搜索树上至少存在两个子节点满足上述性质。

代码模板(求割点个数,及从小到大输出):

#include<bits/stdc++.h>

using namespace std;
const int maxn=1e5+10;
int head[maxn],n,m,tot;
struct Edge
{
    int nex,to;
}edge[maxn<<1];
void add(int from,int to)
{
    edge[++tot].to=to;
    edge[tot].nex=head[from];
    head[from]=tot;
}
int dfn[maxn],low[maxn],st[maxn],idx,root;
bool cut[maxn];
void tarjan(int u)                          //tarjan求一下dfn和low
{
    dfn[u] = low[u] = ++idx;                //初始化dfn和low
    int flag=0;
    for(int i=head[u];i!=-1;i=edge[i].nex)
    {
        int v=edge[i].to;
        if(dfn[v])  low[u]=min(low[u],dfn[v]);  //定义,追溯值为子树最小的时间戳
        else{
            tarjan(v);                          //找子节点的子节点
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v]){                 //判定条件
                flag++;
                if(u!=root||flag>1) cut[u]=true;
            }
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    memset(head,-1,sizeof(head));
    tot=0;
    for(int i=1;i<=m;++i){
        int a,b;
        scanf("%d %d",&a,&b);
        if(a==b)    continue;
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=n;++i){
        if(!dfn[i]){
            root=i;
            tarjan(i);
        }
    }
    int res=0;              //个数
    for(int i=1;i<=n;++i)
        if(cut[i])  res++;
    printf("%d\n",res);
    for(int i=1;i<=n;++i)
        if(cut[i])  printf("%d ",i);
    printf("\n");
}

如果要求连通分量数,还要写个dfs即可

void dfs(int u)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].nex){
        int v=edge[i].to;
        if(vis[v])  continue;
        vis[v]=1;
        dfs(v);
    }
}
for(int i=1;i<=cnt;++i)
{
    if(cut[i])
    {
        int son=0;
        memset(vis,0,sizeof(vis));
        vis[i]=1;
        for(int j=head[i];j!=-1;j=edge[j].nex){
            int v=edge[j].to;
            if(vis[v])  continue;
            dfs(v);
            son++;
        }
        printf("%d\n",son); //son为连通分量数
    }
}

3:Tarjan判断割边

割边判断法则:无向边(x,y)是桥当且仅当搜索树上存在 x 的一个子节点 y ,满足:
\[
dfn[x]<low[y]
\]
与割点不同的是,这里不能取等号。这个不等式其实也很好理解,根据定义\(dfn[x]<low[y]\)说明从\(subtree(y)\)出发,在不经过边\((x,y)\)的前提下,不管走哪条边,都无法到达x或比x更早访问的结点。若把边\((x,y)\)删除,则\(subtree(y)\)就像是形成了一个封闭环境,因此边\((x,y)\)是割边。

#include<bits/stdc++.h>

using namespace std;
const int maxn=1e5+10;
struct Edge
{
    int nex,to;
}edge[maxn<<1];
int n,m,tot,head[maxn];
void add(int from,int to)       //这里边从下标为2开始存方便异或处理
{
    edge[++tot].to=to;
    edge[tot].nex=head[from];
    head[from]=tot;
}
int bridge[maxn<<1],dfn[maxn],low[maxn],idx;
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++idx;
    for(int i=head[u];i!=-1;i=edge[i].nex)
    {
        int v=edge[i].to;
        if(!dfn[v]){
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<low[v])   bridge[i]=bridge[i^1]=true;
        }
        else if(i!=(fa^1))  low[u]=min(low[u],dfn[v]);
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    memset(head,-1,sizeof(head));
    tot=1;                          //注意边的下标从2开始储存
    for(int i=1;i<=m;++i){
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i,-1);
    for(int i=2;i<=tot;i+=2)
        if(bridge[i])   printf("%d %d\n",edge[i].to,edge[i^1].to);
    system("pause");
}

【题意】:给了一幅图,问需要加几条边将这幅图所有的结点都在环上(可能在不同的环上)

求一下割边,判断一下叶结点的数量,答案即为(num+1)>>1;看代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int maxn=1e4+10;
int head[maxn],tot;
struct Edge
{
    int from,nex,to;
}edge[maxn<<1];
void add(int from,int to)
{
    edge[++tot].to=to;
    edge[tot].from=from;
    edge[tot].nex=head[from];
    head[from]=tot;
}
int dfn[maxn],low[maxn],idx;
bool bridge[maxn];
void tarjan(int u,int fa)           //求割边(桥)
{
    dfn[u]=low[u]=++idx;
    for(int i=head[u];i!=-1;i=edge[i].nex)
    {
        int v=edge[i].to;
        if(!dfn[v]){
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<low[v]){
                bridge[i]=bridge[i^1]=true;
            }
        }
        else if(i!=(fa^1))  low[u]=min(low[u],dfn[v]);      //异或打括号!!
    }
}
int n,m,outd[maxn],id[maxn],dcc;     //id为双连通分量编号,outd为出度
void dfs(int u)                      //跑一遍dfs求一下各个边的双连通分量编号
{
    id[u]=dcc;
    for(int i=head[u];i!=-1;i=edge[i].nex)
    {
        int v=edge[i].to;
        if(bridge[i]||id[v])  continue; //双连通分量不包括桥
        dfs(v);
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    tot=1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;++i){
        int a,b;
        scanf("%d %d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i,-1);
    for(int i=1;i<=n;++i)
        if(!id[i]){
            dcc++;
            dfs(i);
        }
    for(int i=2;i<=tot;i+=2)
    {
        int u=edge[i].from,v=edge[i].to;
        if(id[u]!=id[v]){
            outd[id[u]]++;
            outd[id[v]]++;
        }
    }
    int ans=0;                  //叶子结点数量
    for(int i=1;i<=dcc;++i)
        if(outd[i]==1)  ans++;
    printf("%d\n",(ans+1)>>1);
}

原文地址:https://www.cnblogs.com/StungYep/p/12252102.html

时间: 2024-10-10 15:14:21

Tarjan算法与割点割边的相关文章

tarjan算法求割点cojs 8

tarjan求割点:cojs 8. 备用交换机 ★★   输入文件:gd.in   输出文件:gd.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接.因电子设备容易损坏,需给通讯点配备备用交换机.但备用交换机数量有限,不能全部配备,只能给部分重要城市配置.于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备用交换机.请你根据城市线路情况,计算需配备备用交换

『Tarjan算法 无向图的割点与割边』

无向图的割点与割边 定义:给定无相连通图\(G=(V,E)\) 若对于\(x \in V\),从图中删去节点\(x\)以及所有与\(x\)关联的边后,\(G\)分裂为两个或以上不连通的子图,则称\(x\)为\(G\)的割点. 若对于\(e \in E\),从图中删去边\(e\)之后,\(G\)分裂为两个不连通的子图,则称\(e\)为\(G\)的割边. 对于很多图上问题来说,这两个概念是很重要的.我们将探究如何求解无向图的割点与割边. 预备知识 时间戳 图在深度优先遍历的过程中,按照每一个节点第一

割点(Tarjan算法)

本文可转载,转载请注明出处:www.cnblogs.com/collectionne/p/6847240.html .本文未完,如果不在博客园(cnblogs)发现此文章,请访问以上链接查看最新文章. 前言:之前翻译过一篇英文的关于割点的文章(英文原文.翻译),但是自己还有一些不明白的地方,这里就再次整理了一下.有兴趣可以点我给的两个链接. 割点的概念 在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点(cut vertex / articulation

Tarjan算法:求解图的割点与桥(割边)

简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. 割点与桥(割边)的定义 在无向图中才有割边和割点的定义 割点:无向连通图中,去掉一个顶点及和它相邻的所有边,图中的连通分量数增加,则该顶点称为割点. 桥(割边):无向联通图中,去

ZOJ Problem - 2588 Burning Bridges tarjan算法求割边

题意:求无向图的割边. 思路:tarjan算法求割边,访问到一个点,如果这个点的low值比它的dfn值大,它就是割边,直接ans++(之所以可以直接ans++,是因为他与割点不同,每条边只访问了一遍). 需要注意的就是此处有多重边,题目中要求输出确定的不能被删除的边,而多重边的保留不是可以确定的,所以多重边都是不可以被保留的,我们可以在邻接表做一个flag的标记,判断他是不是多重边. 注意建图的时候数组应该是m × 2,因为这里是无向边,当心RE! 注意输出的时候编号是必须要拍好序再输出. 还有

tarjan算法--求无向图的割点和桥

一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中,如果删除某点后,图变成不连通,则称该点为割点. 二:tarjan算法在求桥和割点中的应用 1.割点:1)当前节点为树根的时候,条件是“要有多余一棵子树”(如果这有一颗子树,去掉这个点也没有影响,如果有两颗子树,去掉这点,两颗子树就不连通了.) 2)当前节点U不是树根的时候,条件是“low[v]>=

tarjan求割点割边的思考

这个文章的思路是按照这里来的. 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 一,根节点如果有两颗及以上子树,它就是割点.这个应该说是显然的. 二,对于普通的结点a,如果它递归树的子树中,有任意节点b的low[b]<dfn[a],那么它就不是割点,反之则是割点. 我们先来证明如果low[b]<dfn[a],a一定不是割点.low[b]<dfn[a]说明有一个结点,通过非树枝边可以访问到a以前的结点,那么显然去掉a以后,b依然与a以上的递归树联通,a不是割

求割点 割边 Tarjan

附上一般讲得不错的博客 https://blog.csdn.net/lw277232240/article/details/73251092 https://www.cnblogs.com/collectionne/p/6847240.html https://blog.csdn.net/zhn_666/article/details/77971619 然后附上模板题:              https://vjudge.net/problem/HihoCoder-1183 裸题,直接要你输

tarjan算法(强连通分量 + 强连通分量缩点 + 桥 + 割点 + LCA)

这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量,缩点,桥,割点,LCA等,日后写到相应的模板题我就会放上来. 1.强连通分量(分量中是任意两点间都可以互相到达) 按照深度优先遍历的方式遍历这张图. 遍历当前节点所出的所有边.在遍历过程中: ( 1 ) 如果当前边的终点还没有访问过,访问. 回溯回来之后比较当前节点的low值和终点的low值.将较小