一些概念

无向图:

连通图:在无向图中,任意两点都直接或间接连通,则称该图为连通图。(或者说:任意两点之间都存在可到达的路径)

连通分量: 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算法的更多相关文章

  1. 图的连通性——Tarjan算法&割边&割点

    tarjan算法 原理: 我们考虑 DFS 搜索树与强连通分量之间的关系. 如果结点 是某个强连通分量在搜索树中遇到的第⼀个结点,那么这个强连通分量的其余结点肯定 是在搜索树中以 为根的⼦树中. 被称 ...

  2. 图之强连通--Tarjan算法

    强连通分量 简介 在阅读下列内容之前,请务必了解图论基础部分. 强连通的定义是:有向图 G 强连通是指,G 中任意两个结点连通. 强连通分量(Strongly Connected Components ...

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

    简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边 ...

  4. 关于连通性问题的Tarjan算法暂结

    关于基础知识的预备桥和割点.双联通分量.强连通分量,支配树.(并不会支配树) 关于有向图的Tarjan,是在熟悉不过的了,它的主要功能就是求强联通分量,缩个点,但是要注意一下构建新图的时候有可能出现重 ...

  5. tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)

    基本概念 给定无向连通图G = (V, E)割点:对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点割边(桥)若对于e∈E,从图中删去边e之后,G分 ...

  6. 【强联通图 | 强联通分量】HDU 1269 迷宫城堡 【Kosaraju或Tarjan算法】

      为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明 ...

  7. Tarjan算法:求解无向连通图图的割点(关节点)与桥(割边)

    1. 割点与连通度 在无向连通图中,删除一个顶点v及其相连的边后,原图从一个连通分量变成了两个或多个连通分量,则称顶点v为割点,同时也称关节点(Articulation Point).一个没有关节点的 ...

  8. 图之强连通、强连通图、强连通分量 Tarjan算法

    原文地址:https://blog.csdn.net/qq_16234613/article/details/77431043 一.解释 在有向图G中,如果两个顶点间至少存在一条互相可达路径,称两个顶 ...

  9. 求图的强连通分量--tarjan算法

    一:tarjan算法详解 ◦思想: ◦ ◦做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间 ...

随机推荐

  1. ES6-11学习笔记--Generator

    基本使用 function后面加个*号 function* foo() { for (let i = 0; i < 3; i++) { yield i; // yield不能作为构造函数去使用, ...

  2. jboss修改内存

    在修改配置文件,在 <JBOSS_HOME> /bin/stadalone.conf中      找到并修改  如图

  3. 获取bootstrap模态框点击的对应项(e.relatedTarget.dataset)

    //获取绑定的自定义属性值<ul> <li data-toggle="modal" data-index="电表1111" data-targ ...

  4. pip 和 Conda 镜像站配置

    如果你经常使用 Python,那么你对 pip 和 Conda 一定不陌生,它们作为包管理器,可以非常方便的帮助我们下载需要的 Python 包,但是受限于大多 Python 包的服务器在国外,国内下 ...

  5. python---希尔排序的实现

    def shell_sort(alist): """希尔排序""" n = len(alist) gap = n // 2 # 插入算法执行 ...

  6. CVE 公开披露的网络安全漏洞列表

    CVE®是一份公开披露的网络安全漏洞列表, 官方地址为 : https://cve.mitre.org/cve/ 比如 mavenrepository 上阿里的Druid修复的漏洞的列表如下:

  7. JQuery学习高级

    ## 今日内容:     1. JQuery 高级         1. 动画         2. 遍历         3. 事件绑定         4. 案例         5. 插件 ## ...

  8. Spring 源码 (2)Spring IOC 容器 前戏准备工作

    Spring 最重要的方法refresh方法 根据上一篇文章 https://www.cnblogs.com/redwinter/p/16141285.html Spring Bean IOC 的创建 ...

  9. 手写一个bind

    1 Function.prototype.bind1 = function(){ 2 // 将类数组转化成数组 3 let arr = Array.prototype.slice.call(argum ...

  10. GraphScope v0.12.0 版本发布

    GraphScope 每月进行常规版本的迭代与发布,GraphScope v0.12.0 全新版本在四月如期而至.v0.12.0 为交互式图查询 GAIA 引入全新的 IR 层以及新增 Giraph ...