图论分支-Tarjan初步-割点和割边
所谓割点(顶)割边,我们引进一个概念
割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图。
割边(桥):删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥。
这样大家就应该能简单理解(怎么可能)割点割边了。
所以我们再来看一个图

这样大家就能明白了吧(明白是明白了,但是要他干嘛(自动忽略))到后面会明白的。
然后怎么求,这是一个问题,直接想法是搜索,枚举每一个点,然后再去检验是否联通,这样的复杂度应该是O(n2),很显然很不优秀,万一数据是1e5以上不就凉凉了吗。所以我们就可以引进我们的正题了,low-dfn求割点割边。
怎么求?
那么什么是dfn和low呢,简单解释一下,我们的dfn是一个时间戳,也就是访问的时间,而这个就是Tarjan算法的基础(好像忘介绍Tarjan了)而我们的low就是返祖边,也就是通向以前的点的边,所以说,看图。

还有一个重要概念也就是,母树继承其子树最小的返祖边,而我们可以观察一下我们的dfn值和low值,再去寻找割点会发现一个重要的事实
每一个割点的dfn一定小于等于其子树的low值,而且如果是root,他的子树大于1他即为割点
这样我们的Code就跃然纸上了
Code
void Tarjan(int x){
low[x]=dfn[x]=++t;//记录时间戳,当然low值一开始也要与dfn值相同
for(int i=head[x];i;i=edge[i].next){//链式前向星
int y=edge[i].to;
if(!dfn[y]){//如果没有访问过
Tarjan(y);//搜索查询
low[x]=min(low[x],low[y]);//母树继承子树的最小low值
if(low[y]>=dfn[x]){//发现的规律
flag++;//这个是因为我们的root也有可能是割点,但是它的条件有点苛刻
if(flag>||x!=root) cut[x]=;//如果子树分支大于1,那么无论他是什么都是割点了
}
}else low[x]=min(low[x],dfn[y]);//返祖边,其连接的点继承最小返祖边连接点的dfn值
}
}
备注应该也是很明白了。
然后我们来看割边,上面已经将概念说的很明白了,删边后,图不联通,然后我们根据一个图来理解割边。

有点丑,左边是dfn,右边是low,红边就是割边,观察一下割边两边两个点的dfn和low值情况,我们会惊喜地发现
母点的dfn值小于(严格小于)子点low值,那么这条边就是割边
好的,我们的代码就出来了
这里引进一道例题

Code
#include<bits/stdc++.h>
using namespace std;
int n,m,head[],cent=,low[],dfn[],t,cnt;
struct node{
int next,to;
}edge[];
struct prin{
int u,v;
}ans[];//记录答案 void add(int u,int v){
edge[++cent]=(node){head[u],v};head[u]=cent;
} bool operator <(prin a,prin b){
if(a.u!=b.u) return a.u<b.u;
else return a.v<b.v;
}//重载运算符 ,sort要用 void Tarjan(int x,int fa){//fa定义的是我们走的上一条边
low[x]=dfn[x]=++t;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
Tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) {
ans[++cnt]=x<y?(prin){x,y}:(prin){y,x};//如果结果成立,那么就可以记录这条边的两个点
} //这里用到了格式转换,prin的定义在上面
}else if((i^)!=fa){//由于双向建边,需要cent=1,然后用^来验边,或者将fa定义成上一个点
low[x]=min(low[x],dfn[y]);//
}
}
return ;
} int main(){
freopen("danger.in","r",stdin);
freopen("danger.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
if(a==b) continue;//去除自环
add(a,b),add(b,a);
}
for(int i=;i<=n;i++){
if(!dfn[i]) Tarjan(i,);//不连通时Tarjan,一般都是这样写
}
sort(ans+,ans+cnt+);//题目要求
for(int i=;i<=cnt;i++){
printf("%d %d\n",ans[i].u,ans[i].v);//输出答案
}
return ;
}
这应该就很显然了。
模板讲完了,但还是希望自己的Code要有自己的风格,可以借鉴,但不能照搬。
例题
我们先来看一下割点

——嗅探器
这个显然是要找割点的,那么怎么找呢?
我们的最大问题是有两个点,在去掉割点后,如何检验他们是否在同一个联通块中?
我们首先的思路是暴力枚举割点,然后检验联通,大概能得10%的分,那么我们来想正解
我们可以以a为根,那么就解决了两个点的问题,但是检验呢?
我们可以将Tarjan定义为bool类型即可,然后在Tarjan的过程中,回溯检验值,详情看代码;
Code
#include<bits/stdc++.h>
using namespace std;
int n,a,b,head[],cent,root,fin,low[],dfn[];
int t,cut[];
struct node{
int next,to;
}edge[]; void add(int u,int v){
edge[++cent]=(node){head[u],v};head[u]=cent;
} bool Tarjan(int x){
low[x]=dfn[x]=++t;
bool f1=(x==fin);// fin是b点,f1是标记 ,检验有没有b点
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
bool z=Tarjan(y);f1|=z;//回溯标记
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]&&x!=root&&x!=fin&&z){//这里割点一定不是root
cut[x]=;
}
}else{
low[x]=min(low[x],dfn[y]);
}
}
return f1;//回溯标记
} int main(){
// freopen("dfnIn.in","r",stdin);
// freopen("dfnin.out","w",stdout);
scanf("%d",&n);
while(scanf("%d%d",&a,&b)&&(a!=&&b!=)){
if(a==b) continue;
add(a,b),add(b,a);
}
scanf("%d%d",&root,&fin);
Tarjan(root);
for(int i=;i<=n;i++){
if(cut[i]&&low[fin]>=dfn[i]){//检验
printf("%d",i);//输出
return ;
}
}
printf("No solution");
}
这样就让我们对Tarjan的灵活运用有更深理解。
然后来看割边例题
......
其实上面的板子题就是我们的例题(逃)
好了,Tarjan的割点和割边就结束了。
可以自行找些例题,寻找灵感。
图论分支-Tarjan初步-割点和割边的更多相关文章
- 图论分支-Tarjan初步-边双联通分量
本来应该先说强连通分量,但是有一定的分配,所以这个在下一篇博客将会见到. 这个本想连点连通分量一起讲,但是量有点大,所以我就分两步讲. 我们先看定义 再来看看图解 很容易就能发现,只要将割边断掉,然后 ...
- 图论分支-Tarjan初步-点双连通分量
上一次我们讲到了边双,这次我们来看点双. 说实话来说,点双比边双稍微复杂一些: 学完边双,我们先看一道题 第一问都不用说了吧,多余的道路,明显的割边. 是不是首先想到用边双,但是我们来看一个图: 有点 ...
- tarjan求割点与割边
tarjan求割点与割边 洛谷P3388 [模板]割点(割顶) 割点 解题思路: 求割点和割点数量模版,对于(u,v)如果low[v]>=dfn[u]那么u为割点,特判根结点,若根结点子树有超过 ...
- hihoCoder 1183 连通性一·割边与割点(Tarjan求割点与割边)
#1183 : 连通性一·割边与割点 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢 ...
- Tarjan在图论中的应用(二)——用Tarjan来求割点与割边
前言:\(Tarjan\) 求割点和割边建立在 \(Tarjan\)算法的基础之上,因此建议在看这篇博客之前先去学一学\(Tarjan\). 回顾\(Tarjan\)中各个数组的定义 首先,我们来回顾 ...
- 『Tarjan算法 无向图的割点与割边』
无向图的割点与割边 定义:给定无相连通图\(G=(V,E)\) 若对于\(x \in V\),从图中删去节点\(x\)以及所有与\(x\)关联的边后,\(G\)分裂为两个或以上不连通的子图,则称\(x ...
- POJ1144 tarjan+网络中割点与割边的数量
题目链接:http://poj.org/problem?id=1144 割点与割边的数量我们可以通过tarjan的思想从一个点开始对其余点进行访问.访问的顺序构成一棵dfs树,其中根节点到任何一个结点 ...
- Tarjan 强连通分量 及 双联通分量(求割点,割边)
Tarjan 强连通分量 及 双联通分量(求割点,割边) 众所周知,Tarjan的三大算法分别为 (1) 有向图的强联通分量 (2) 无向图的双联通分量(求割点,桥) ...
- Tarjan 算法求割点、 割边、 强联通分量
Tarjan算法是一个基于dfs的搜索算法, 可以在O(N+M)的复杂度内求出图的割点.割边和强联通分量等信息. https://www.cnblogs.com/shadowland/p/587225 ...
随机推荐
- 【数学建模】day02-整数规划
基本类似于中学讲的整数规划--线性规划中变量约束为整数的情形. 目前通用的解法适合整数线性规划.不管是完全整数规划(变量全部约束为整数),还是混合整数规划(变量既有整数又有实数),MATLAB都提供了 ...
- Android组件化、模块化、插件化
组件:指的是单一的功能组件,如地图组件(MapSDK).扫码组件(QRCode).支付组件(AnjukePay).路由组件(Router)等等: 模块:指的是独立的业务模块,如新房模块(NewHous ...
- 图片文字识别aip的一个小Demo
目前接触到了一个新的内容,识别图片上的文字,以下是这个Demo 首先需要在需要在百度云-管理中心创建应用 地址:http://console.bce.baidu.com/ai/#/ai/ocr/app ...
- Marriage Match III HDU - 3277(二分权值 + 拆点 建边)
题意: 只不过是hdu3081多加了k种选择 想一下,最多能玩x轮,是不是就是每个女生能最多选x个男生 现在题中的每个女生比3081多了k中选择 那就把女生拆点 i i‘ i --> i ...
- 数字平滑 前端插件JS&CSS库
CDN DEMO 拷贝可用: <!DOCTYPE html> <link rel="stylesheet" href="https://cdn.boot ...
- nginx+php使用open_basedir限制站点目录防止跨站
以下三种设置方法均需要PHP版本为5.3或者以上.方法1)在Nginx配置文件中加入 fastcgi_param PHP_VALUE "open_basedir=$document_root ...
- 【hdu 4658】Integer Partition (无序分拆数、五边形数定理)
hdu 4658 Integer Partition 题意 n分拆成若干个正整数的和,每个正整数出现小于k次,分拆方案有多少.(t<=100,n<=1e5) 题解 之前写过一篇Partit ...
- 面试---Python中的模块和包是什么?
python模块是: 自我包含并且有组织的代码片段为模块. 表现形式为:写的代码保存为文件.这个文件就是一个模块.sample.py 其中文件名smaple为模块名字. python包是: 包是一个有 ...
- luogu1397 [NOI2013]矩阵游戏 (等比数列求和)
一个比较显然的等比数列求和,但有一点问题就是n和m巨大.. 考虑到他们是在幂次上出现,所以可以模上P-1(费马小定理) 但是a或c等于1的时候,不能用等比数列求和公式,这时候就要乘n和m,又要变成模P ...
- 低电平ViL
低电平 编辑 低电平(Vil)指的是保证逻辑门的输入为低电平时所允许的最大输入低电平,当输入电平低于Vil时,则认为输入电平为低电平. 中文名 低电平 外文名 Vil 主要应用 测量电缆和保护连接 ...