Tarjan求割点(割顶) 割边(桥)
割点的定义:
感性理解,所谓割点就是在无向连通图中去掉这个点和所有和这个点有关的边之后,原先连通的块就会相互分离变成至少两个分离的连通块的点。
举个例子: 图中的4号点就是割点,因为去掉4号点和有关边之后连通块{1,2,3} {5} {6}就相互分离了。

图片来自:一篇写的较好的blog:https://www.cnblogs.com/jason2003/p/7603886.html
Tarjan算法求割点:
有好多个Tarjan算法,不要傻傻分不清~~
其实和有向图求强连通分量的Tarjan算法差不多啦,也用到了dfn和low。
因为是无向连通图,所以横向边是没有意义的,(有反向边的存在)。
dfn[u]:u在搜索树中被遍历到的次序号(时间戳)。
low[u]:u或u的子树中的结点经过最多一条后向边能追溯到的最早的树中结点的次序号(dfn),有点懵,先不管他,感性理解就是通过边到达的点的最小时间戳。
割点判断条件:
①:若u为树的树根,那么只要u有两个及以上的子节点,那么只要u点消失,子节点所在的连通块就会分离了。
如果只有一个子节点,那么u消失之后还剩下那个子节点的连通块,仍是一个并不是多个。
②:若u不为树根,v不为u的父结点,当dfn[u]<=low[v]时,u就为割点,由该式子的含义可得,v以及v的子树最多只能到达u结点,
不能到达u的祖先,此时删掉u,那么(v及v的子树)和(u的祖先)就会相互分离了,自然u就是割点。
求割点时若(u,v)为后向边,v不为u的父结点,low[u]=min{low[u],dfn[v]},里面一定要写dfn[v],不能写low[v]。
原因详见:https://www.luogu.org/blog/ztyluogucpp/solution-p3388
割点模板:
L3388 【模板】割点(割顶):https://www.luogu.org/problemnew/show/P3388
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ll long long
#define maxn 100009
inline ll read()
{
ll x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-') f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=(x<<)+(x<<)+(ll)(ch-'');ch=getchar();}
return x*f;
}
int head[maxn],dfn[maxn],low[maxn],point[maxn];
int n,m,k,ans,cnt,id,tot,root;
struct edge
{
int to,nxt;
}p[maxn<<]; void add(int x,int y)
{
++cnt,p[cnt].to=y,p[cnt].nxt=head[x],head[x]=cnt;
} void Tarjan(int u,int fa)
{
dfn[u]=low[u]=++id;
int child=;
for(int i=head[u];i;i=p[i].nxt)
{
int v=p[i].to;
if(!dfn[v])
{
Tarjan(v,u);
low[u]=min(low[u],low[v]);
if(u!=root&&low[v]>=dfn[u])
point[u]=;
if(u==root&&++child>=)
point[u]=;
}
else
low[u]=min(low[u],dfn[v]);
} }
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read(),m=read();
for(int i=;i<=m;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])
root=i,Tarjan(i,i);
for(int i=;i<=n;i++)
if(point[i])
tot++;
printf("%d\n",tot);
for(int i=;i<=n;i++)
if(point[i])
printf("%d ",i);
fclose(stdin);
fclose(stdout);
return ;
}
割边的定义:
和割点的定义类似,只不过是把去掉点以及与点有关的边改成了去掉这条边看是否连通就行啦。
Tarjan算法求割边:
和求割点只有一点点小区别...
因为有可能存在重边的问题,所以要将一条无向边拆为两条编号一样的有向边,用邻接表进行储存,在判断(u,v)是否为后向边的时候要注意判断是树枝边的反向边还是新的一条边。
割边判断条件:
dfn[u]<low[v],不可以取等号,因为取等号意味着v及v的子树能够到达结点u,那么(u,v)这条边自然就不是割边了。
割边模板:
L1656 炸铁路:https://www.luogu.org/problemnew/show/P1656
裸的割边模板,虽然不需要判断重边但是还是加上比较好,再用优先队列维护一下答案就可以了。
判断是否是树枝边的反向边的时候只需要判断vis[i^1]是否等于1就行了,因为是这样判断的,所以在建边的时候cnt必须从1开始,因为0^1=0,并不会得到1这条反向边。
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 159
#define maxm 5009
inline ll read()
{
ll x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=(x<<)+(x<<)+(ll)(ch-'');ch=getchar();}
return x*f;
}
priority_queue<pair<int,int> >q;
int head[maxn],dfn[maxn],low[maxn];
bool vis[maxm<<];
int n,m,k,ans,tot,id,cnt=;
struct edge
{
int to,nxt;
}p[maxm<<]; void add(int x,int y)
{
p[++cnt]={y,head[x]},head[x]=cnt;
} void Tarjan(int u)
{
dfn[u]=low[u]=++id;
for(int i=head[u];i;i=p[i].nxt)
{
if(vis[i^])
continue;
int v=p[i].to;
vis[i]=;
if(!dfn[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v])
q.push(make_pair(-min(u,v),-max(u,v)));
}
else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read(),m=read();
for(int i=;i<=m;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])
Tarjan(i);
while(q.size())
{
printf("%d %d\n",-q.top().first,-q.top().second);
q.pop();
}
fclose(stdin);
fclose(stdout);
return ;
}
Tarjan求割点(割顶) 割边(桥)的更多相关文章
- 洛谷P3388 【模板】割点(割顶)(tarjan求割点)
题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照 ...
- Tarjan求割点和桥
by szTom 前置知识 邻接表存储及遍历图 tarjan求强连通分量 割点 割点的定义 在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多, ...
- tarjan求割点与割边
tarjan求割点与割边 洛谷P3388 [模板]割点(割顶) 割点 解题思路: 求割点和割点数量模版,对于(u,v)如果low[v]>=dfn[u]那么u为割点,特判根结点,若根结点子树有超过 ...
- $割点割顶tarjan$
原题 #include <bits/stdc++.h> using namespace std; typedef long long LL; inline LL read () { LL ...
- tarjan求割点割边的思考
这个文章的思路是按照这里来的.这里讨论的都是无向图.应该有向图也差不多. 1.如何求割点 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 根节点如果有两颗及以上子树,它 ...
- UESTC 900 方老师炸弹 --Tarjan求割点及删点后连通分量数
Tarjan算法. 1.若u为根,且度大于1,则为割点 2.若u不为根,如果low[v]>=dfn[u],则u为割点(出现重边时可能导致等号,要判重边) 3.若low[v]>dfn[u], ...
- poj_1144Network(tarjan求割点)
poj_1144Network(tarjan求割点) 标签: tarjan 割点割边模板 题目链接 Network Time Limit: 1000MS Memory Limit: 10000K To ...
- POJ 1144 Network(无向图的割顶和桥模板题)
http://poj.org/problem?id=1144 题意: 给出图,求割点数. 思路: 关于无向图的割顶和桥,这篇博客写的挺不错,有不懂的可以去看一下http://blog.csdn.net ...
- POJ 1144 Network(Tarjan求割点)
Network Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 12707 Accepted: 5835 Descript ...
随机推荐
- Windows编写的shell脚本,在linux上无法执行
前两天由于要查一个数据库的binlog日志,经常用命令写比较麻烦,想着写一个简单的脚本,自动去刷一下数据库的binlog日志,就直接在windows上面写了,然后拷贝到linux中去运行,其实很简单的 ...
- 我眼中的K-近邻算法
有一句话这样说:如果你想了解一个人,你可以从他身边的朋友开始. 如果与他交往的好友都是一些品行高尚的人,那么可以认为这个人的品行也差不了. 其实古人在这方面的名言警句,寓言故事有很多.例如:人以类聚, ...
- Python并发编程之同步\异步and阻塞\非阻塞
一.什么是进程 进程: 正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 进程和程序的区别: 程序仅仅只是一堆代码而已,而进程指的是程序的运行过程. 需要强调的是:同一个程序执行两次,那也 ...
- HTML(一)HTML基础语法(HTML简介,HTML文档声明)
HTML 概念介绍 [概念] (Hyper Text Markup Language)超文本标记语言,是用来描述网页的一种语言 超文本(Hyper Text):不只包括文本,也可以包括图片.链接.音乐 ...
- JDBC事务管理
JDBC事务管理 概念回顾: 事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,那么这个业务操作的多个步骤要么同时失败,要么同时成功 事务操作: 开启事务 提交事务 回滚事务 使用Conn ...
- SpringBoot系列: CommandLineRunner接口的用处
========================================使用 CommandLineRunner 对Spring Bean进行额外初始化==================== ...
- latex beamer 添加页码
导言中加 \setbeamertemplate{footline}[frame number]
- luogu 4345 Lucas的变形应用
求 sigma i由0-k C(n,i) 利用Lucas定理+整除分块将C(n/p,i/p)利用i/p分块,得到k/p-1个整块(p-1)和一个小块(k%p) 最后得到式子 F(n,k)=F(n/p, ...
- H5取经之路——HTML的基本标签
一.head中的基本标签 1.HTML文档的结构: a.<head>头部部分,b.<body>主体部分 <!DOCTYPE html> <!-- ↑为 ...
- 2.11 while循环的嵌套以及应用(难)
while循环嵌套 前面学习过if的嵌套了,想一想if嵌套是什么样子的? 类似if的嵌套,while嵌套就是:while里面还有while <1>while嵌套的格式 while 条件1: ...