图的连通性--Tarjan算法
一些概念
无向图:
连通图:在无向图中,任意两点都直接或间接连通,则称该图为连通图。(或者说:任意两点之间都存在可到达的路径)
连通分量: G的 最大连通子图 称为G的连通分量。
有向图 (ps.区别在与“强”)
强连通图: 在有向图中,对于每一对顶点Vi,Vj都存在从Vi到Vj和从Vj到Vi的路径(任意两点之间都存在可到达对方的路径),则称该图为强连通图。
强连通分量: 有向图G的 最大强连通子图 称为G的强连通分量。
求强连通分量+有向图的压缩(缩点)
缩点即讲一个强连通分量中的点标记为同一类,看作一个大点包含所有其中点的信息
可以用Kosaraju但是,这里讲的是精妙的Tarjan算法
背景:dfs序
思想:做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点及其下方节点所能到达的开始时间最早的节点的 开始时间 。初始时dfn[i]=low[i]
ps.大家一开始不要过于纠结low的意思,很多时候是根据需求和用途来的
important:
如果一个节点的low值等于dfn值,则说明其下方的节点不能走到其上方的节点,那么该节点u就是一个强连通分量在DFS搜索树中的根。
但是u的子孙节点未必就和u处于同一个强连通分量,怎样找出u的子孙中,哪些跟它位于同一强连通分量?
这里需要用到栈
栈的用途是保存搜过的结点并能及时删除,先上代码
int dfn[N],low[N],Time,SCC=0,Belong[N],In_du[N];
bool In_stack[N];
stack<int> st;
void Tarjan(int u) {
dfn[u]=low[u]=++Time;
st.push(u); In_stack[u]=true;
for(int v=1;v<=n;v++) {
if(mp[u][v]&&!dfn[v]) {
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(mp[u][v]&&dfn[v]!=0&&In_stack[v]) {
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]) {
++SCC; int v;
do {
v=st.top(); st.pop();
In_stack[v]=false; Belong[v]=SCC; //belong 缩点
}while(v!=u); //直到删除了最高点
}
}
注意的是理解代码的In_stack[]标记,
因为如果你不可以反祖边指向了一个被遍历过且已经出栈的结点。(一个点一旦出了栈,它便有了belong)
缩点使图缩为DAG图
因此拓扑排序(图上的动态规划),在此前许会用缩点预处理
此处设一道基础例题:【APIO2009】抢掠计划
题意是:有向图中,有些点是酒吧,有的不是。每一个点都有它的价值,不过你只能抢一次并加上该价值。你从s出发到任意一个有酒吧的点结束。问您能抢到的最大价值是多少。
方程是:dp[v] = Max{ dp[u]+len[u][v]; }
为了保证无后效性:按照 Tarjan缩点,处理新图,拓扑排序中DAG动态规划/记忆化搜索 的顺序,来码题:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int head[N],to[N],tot,nxt[N],val[N],jb[N];
int Head[N],To[N],Tot,Nxt[N],f[N];
int Time,dfn[N],low[N],Belong[N],size[N],SCC,In[N];
stack<int> st;
queue<int> Q;
bool Instack[N];
void add_edge(int u,int v) {
tot++; nxt[tot]=head[u]; to[tot]=v; head[u]=tot;
}
void Add_edge(int u,int v) {
Tot++; Nxt[Tot]=Head[u]; To[Tot]=v; Head[u]=Tot;
}
void Tarjan(int u) {
dfn[u]=low[u]=++Time;
st.push(u); Instack[u]=true;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(!dfn[v]) {
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(Instack[v]) {
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]) {
SCC++; int v;
do {
v=st.top(); st.pop(); Instack[v]=false;
Belong[v]=SCC; size[SCC]+=val[v];
}while(v!=u);
}
}
int main() {
int n,m,s,p;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d%d",&u,&v);
add_edge(u,v);
}
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
scanf("%d%d",&s,&p);
for(int i=1;i<=p;i++) scanf("%d",&jb[i]);
Tarjan(s); //这样被缩的点(有belong的点)都能被s到达
for(int u=1;u<=n;u++) {
for(int j=head[u];j;j=nxt[j]) {
int v=to[j];
if(!Belong[u]||!Belong[v]) continue;
if(Belong[u]!=Belong[v]) {
Add_edge(Belong[u],Belong[v]); In[Belong[v]]++;
}
}
}
Q.push(Belong[s]);
for(int i=1;i<=SCC;i++) {
f[i]=size[i];
}
while(!Q.empty()) {
int u=Q.front(); Q.pop();
for(int j=Head[u];j;j=Nxt[j]) {
int v=To[j];
f[v]=max(f[v],f[u]+size[v]);
In[v]--; if(!In[v]) Q.push(v);
}
}
int ans=0;
for(int i=1;i<=p;i++) {
if(Belong[jb[i]]) {
ans=max(ans,f[Belong[jb[i]]]);
}
}
printf("%d",ans);
return 0;
}
再讲一道有趣的强连通分量题目吧。
洛谷 P1407 稳定婚姻
题目大概讲了2n个点,其中有n对夫妻,又有m对情侣,请问能否在第i对夫妻离婚的情况下,所有人都能找到对象。
思路:将每对夫妻(女->男)建有向图,再将每对情侣按照(男->女)连边,【环的点数都为偶】 判断每队夫妻是否在同一个一个强连通分量上,这样它们不在一起时,才能错序排列以配偶。
因此,代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],b[N];
int num,tot,ng,nb,head[N],nxt[N],to[N],cnt;
int Time,dfn[N],low[N],Belong[N],SCC;
bool Instack[N];
stack<int> st;
void add_edge(int u,int v) {
tot++; nxt[tot]=head[u]; to[tot]=v; head[u]=tot;
}
void Tarjan(int u) {
dfn[u]=low[u]=++Time;
st.push(u); Instack[u]=true;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(!dfn[v]) {
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(Instack[v]) {
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]) {
SCC++; int v;
do {
v=st.top(); st.pop(); Instack[v]=false;
Belong[v]=SCC;
}while(v!=u);
}
}
map<string,int> mp;
int main() {
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
string p,q;
cin>>p>>q;
mp[p]=++num;
mp[q]=++num;
a[i]=mp[p]; b[i]=mp[q];
add_edge(a[i],b[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++) {
string p,q;
cin>>p>>q;
int u=mp[p],v=mp[q];
add_edge(v,u);
}
for(int i=1;i<=n*2;i++) {
if(!dfn[i]) {
Tarjan(i);
}
}
for(int i=1;i<=n;i++) {
if(Belong[a[i]]!=Belong[b[i]]) {
printf("Safe\n");
}
else printf("Unsafe\n");
}
return 0;
}
图的连通性--Tarjan算法的更多相关文章
- 图的连通性——Tarjan算法&割边&割点
tarjan算法 原理: 我们考虑 DFS 搜索树与强连通分量之间的关系. 如果结点 是某个强连通分量在搜索树中遇到的第⼀个结点,那么这个强连通分量的其余结点肯定 是在搜索树中以 为根的⼦树中. 被称 ...
- 图之强连通--Tarjan算法
强连通分量 简介 在阅读下列内容之前,请务必了解图论基础部分. 强连通的定义是:有向图 G 强连通是指,G 中任意两个结点连通. 强连通分量(Strongly Connected Components ...
- Tarjan算法:求解图的割点与桥(割边)
简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边 ...
- 关于连通性问题的Tarjan算法暂结
关于基础知识的预备桥和割点.双联通分量.强连通分量,支配树.(并不会支配树) 关于有向图的Tarjan,是在熟悉不过的了,它的主要功能就是求强联通分量,缩个点,但是要注意一下构建新图的时候有可能出现重 ...
- tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)
基本概念 给定无向连通图G = (V, E)割点:对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点割边(桥)若对于e∈E,从图中删去边e之后,G分 ...
- 【强联通图 | 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】
为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明 ...
- Tarjan算法:求解无向连通图图的割点(关节点)与桥(割边)
1. 割点与连通度 在无向连通图中,删除一个顶点v及其相连的边后,原图从一个连通分量变成了两个或多个连通分量,则称顶点v为割点,同时也称关节点(Articulation Point).一个没有关节点的 ...
- 图之强连通、强连通图、强连通分量 Tarjan算法
原文地址:https://blog.csdn.net/qq_16234613/article/details/77431043 一.解释 在有向图G中,如果两个顶点间至少存在一条互相可达路径,称两个顶 ...
- 求图的强连通分量--tarjan算法
一:tarjan算法详解 ◦思想: ◦ ◦做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间 ...
随机推荐
- 移动端H5页面中1px边框的几种解决方法
问题提出 这是一个比较老的问题了,我第一次注意到的时候,是UI设计师来找我麻烦,emmm那时候我才初入前端职场,啥也不懂啊啊啊啊啊,情形是这样的:设计师拿着手机过来:这些边框都粗了啊,我的设计稿上是1 ...
- Pullword 中文分词
安装 npm install pullword 使用 var defaultOptions = { url: 'http://api.pullword.com/post.php', /* api ...
- 文档声明(Doctype)和<!Doctype html>有何作用? 严格模式与混杂模式如何区分?它们有何意义?
文档声明的作用: 文档声明是为了告诉浏览器,当前HTML文档使用什么版本的HTML来写的,这样浏览器才能按照声明的版本来正确的解析. <!doctype html> 的作用就是让浏览器进入 ...
- MySQL的安装详细教程
一.下载MySQL数据库并创建初始化文件 1.下载MySql数据压缩包-----下载网址:https://dev.mysql.com/downloads/mysql/ 2.选择兆数最少的那个下载 3. ...
- Vue整合axios 插件方式
1 创建一个vue的项目 使用命令 vue create axios-vue 创建,可以什么都不用勾选 2 安装axios npm install axios --save 如果安装过程很慢的话,也可 ...
- Blazor 国际化多语言界面 (I18nText )
在实际使用中,我们经常会遇到需要把程序界面多种语言切换,适应不同地区使用者的需求,本文介绍一个我初学Blazor接触到的库,边撸边讲解. 包名: Toolbelt.Blazor.I18nText ht ...
- python版本共存与语法的注释
python的多种版本共存 首先还是先下载python解释器除最高版本的另外两个版本 个人推荐的是 3.6.8和2.7.14 首先我电脑是win7系统 在计算机属性右键点开高级设置点击环境变量 将下载 ...
- findmnt、lsblk、mount 命令查看磁盘、目录挂载、挂载点以及文件系统格式等情况
findmnt 展示出了目标挂载点( TARGET ).源设备( SOURCE ).文件系统类型( FSTYPE )以及相关的挂载选项( OPTIONS ),例如文件系统是否是可读可写或者只读的.根( ...
- @RequestBody和@RequestParam注解以及返回值,ajax相关知识点
关于前后端传递json数据这块查了好多资料,好多地方还是不清楚,先记录一下清楚的地方. 如果我们前端使用ajax发json数据,一般都加上contentType:'application/json;c ...
- 2021.11.30 eleveni的水省选题的记录
2021.11.30 eleveni的水省选题的记录 因为eleveni比较菜,eleveni决定先刷图论,再刷数据结构,同时每天都要刷dp.当然,对于擅长的图论,eleveni决定从蓝题开始刷.当然 ...