tarjan算法是在dfs生成一颗dfs树的时候按照访问顺序的先后,为每个结点分配一个时间戳,然后再用low[u]表示结点能访问到的最小时间戳

以上的各种应用都是在此拓展而来的。

割点:如果一个图去掉某个点,使得图的连通分支数增加,那么这个点就是割点

某个点是割点,当且仅当这个点的后代没有连回自己祖先的边。即low[v] >= dfn[u]     , v是u的后代

需要注意的是根结点的特判,因为根结点没有祖先,根结点是割点,当且仅当根结点有两个以上的儿子。

问题:重边对该算法有影响吗?没有影响。

   需要注意的地方? 图至少有三个点以上, 否则需要注意一下。

  

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
vector<int> g[N];
int dfs_clock,dfn[N],low[N];
bool isCut[N];
void init(int n)
{
dfs_clock = ;
for(int i=; i<=n; ++i)
dfn[i] = low[i] = isCut[i] = ;
}
//重边对割点没有影响,且该算法对有向图同样适用
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dfs_clock;
int child = ;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa) continue;//如果是树枝边的反向访问,则不能用来更新low[u]
child++;
if(dfn[v]==)
tarjan(v,u);
low[u] = min(low[u],low[v]);//用树枝边,或者后向边来跟新low[u]
if(low[v] >= dfn[u])
isCut[u] = true;
}
if(fa==- && child>=) isCut[u] = true;
}
int main()
{
int n,m,i,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
tarjan(,-);
for(i=; i<=n; ++i)
if(isCut[i])
printf("%d ",i);
puts("");
}
return ;
}

割边:如果一个图去掉某条边,使得图的连通分支数增加,那么这条边就是割边(桥)

某条边是割边,当且仅当某个点的后代没有连回自己或者自己祖先的边,即low[v] > dfn[u],  那么边(u,v)是割边

问题:重边对该算法有影响吗? 有影响。 所以要判断是不是有重边

  

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
vector<int> g[N];
int dfs_clock,dfn[N],low[N],cntCut;
void init(int n)
{
cntCut = dfs_clock = ;
for(int i=; i<=n; ++i)
{
dfn[i] = low[i] = ;
g[i].clear();
}
}
//重边对割边有影响,比如有2--3 ,2--3两条边,那么边2--3就不是割边,因为去掉还是连通的,所以要判断一下
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dfs_clock;
bool flag = false;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa && !flag)//在这里判断有没有重边
{
flag = true;
continue;
}
if(dfn[v]==)
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] > dfn[u])//这里统计的是割边的数量,如果要记录割边,那么就标记边,或者把边入栈
cntCut++;
}
}
int main()
{
int n,m,i,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
tarjan(,-);
printf("%d\n",cntCut);
}
return ;
}

点-双连通分量:如果任意两点存在两条点不重复的路径,那么就说这个图是点-双连通的。点-双连通的极大子图称为双连通分量

双连通分量之间的分界点是割点。而且双连通分量不可能分布在树根结点两端。所以我们将边入栈,当遇到割点时,就将边出栈,直到有边等于当前边。就跳出

问题:重边对该算法有影响吗? 没有影响

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
struct Edge
{
int u,v;
Edge(){};
Edge(int u, int v)
{
this->u = u;
this->v = v;
}
};
vector<int> g[N],bcc[N];
int bccno[N],dfn[N],low[N],dfs_clock,cnt;
stack<Edge> st;
void init(int n)
{
cnt = dfs_clock = ;
for(int i=; i<=n; ++i)
{
bccno[i] = low[i] = dfn[i] = ;
bcc[i].clear();
g[i].clear();
}
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dfs_clock;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(dfn[v]==)
{
st.push(Edge(u,v));
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u])//如果这个点是割点,那么先前入栈的一些边是属于一个双连通分量的
{
Edge x;
cnt++;
for(;;)
{
x = st.top();
st.pop();
if(bccno[x.u] != cnt)
{
bccno[x.u] = cnt;
bcc[cnt].push_back(x.u);
}
if(bccno[x.v] != cnt)
{
bccno[x.v] = cnt;
bcc[cnt].push_back(x.v);
}
if(x.u==u && x.v==v)
break;
}
}
}
else if(v!=fa && dfn[v] < dfn[u])
{
st.push(Edge(u,v));
low[u] = min(low[u],dfn[v]);
} }
}
int main()
{
int n,m,i,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
tarjan(,-);
for(i=; i<=cnt; ++i)
{
printf("bcc %d has vertex:",i);
while(!bcc[i].empty())
{
printf("%d ",bcc[i].back());
bcc[i].pop_back();
}
puts("");
}
}
return ;
}

边-双连通分量:如果任意两点存在两条边不重复的路径,那么就说这个图是边-双连通的,边-双连通的极大子图成为边双连通分量。

边双连通分量的分界点是割边。双连通分量可以分布在根结点的两端。所以不能在for(int i=0; i<g[u].size(); ++i) 这个循环里面判断割边,而要在外面递归返回时判断

问题:重边对该算法有影响吗?有影响,就好像影响割边算法一样

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
vector<int> g[N];
stack<int> st;
int dfn[N],low[N],dfs_clock,bccno[N],cnt;
void init(int n)
{
cnt = dfs_clock = ;
for(int i=; i<n; ++i)
{
dfn[i] = low[i] = ;
bccno[i] = ;
g[i].clear();
}
} void tarjan(int u, int fa)//边双连通分量可能分布在某子树树根的两个分支上
{
low[u] = dfn[u] = ++dfs_clock;
st.push(u);
bool flag = ;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa && !flag) //重边对边连通分量的判断有影响,所以要判断一下
{
flag = true;
continue;
}
if(dfn[v]==)
tarjan(v,u);
low[u] = min(low[u],low[v]);
}
if(dfn[u]==low[u])//说明这个点的所有后代都没有连回自己祖先的边,
{
cnt++;
for(;;)
{
int x = st.top();
st.pop();
bccno[x] = cnt;
if(x==u)
break;
}
}
}
int main()
{
int n,m,i,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
tarjan(,-);
for(i=; i<=n; ++i)
printf("vexter %d belong to bcc %d\n",i,bccno[i]);
}
return ;
}

问添加最少多少条边,使得整个图边-双连通, 首先求出边-双连通分量,然后缩点,最后变成一棵树,那么要加的边 就是(叶子结点+1)/2

强连通分量:如果有向图的任意两点可以,那么就说这个图是强连通的,一个图的极大子图是强连通的,那么就说这个子图是强连通分量

对于一个scc,我们要判断哪个点是该scc最先被发现的点,然后将后来发现的点出栈,知道遇到这个点。 那么出栈的点都属于一个强连通分量

问题:重边对该算法有影响吗?没有影响

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
vector<int> g[N];
stack<int> st;
int cnt,dfs_clock,dfn[N],low[N],sccno[N];
void init(int n)
{
cnt = dfs_clock = ;
for(int i=; i<=n; ++i)
{
dfn[i] = low[i] = sccno[i] = ;
g[i].clear();
}
} void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dfs_clock;
st.push(u);
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(dfn[v]==)
{
tarjan(v,u);
low[u] = min(low[u],low[v]);
}
else if(sccno[v]==)//因为有向图存在横插边,不能用横插边来更新low[u]
{
low[u] = min(low[u],low[v]);
}
}
//同样,因为强连通分量可以分布在根结点的两个分支上,所以在递归返回的时候调用
if(low[u] == dfn[u])
{
cnt++;
for(;;)
{
int x = st.top();
st.pop();
sccno[x] = cnt;
if(x==u)
break;
}
}
}
int main()
{
int n,m,i,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
tarjan(,-);
for(i=; i<=n; ++i)
printf("vertex %d belong to scc %d\n",i,sccno[i]);
}
return ;
}

tarjan算法(割点/割边/点连通分量/边连通分量/强连通分量)的更多相关文章

  1. Tarjan算法求解桥和边双连通分量(附POJ 3352 Road Construction解题报告)

     http://blog.csdn.net/geniusluzh/article/details/6619575 在说Tarjan算法解决桥和边双连通分量问题之前我们先来回顾一下Tarjan算法是如何 ...

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

    题意:求无向图的割边. 思路:tarjan算法求割边,访问到一个点,如果这个点的low值比它的dfn值大,它就是割边,直接ans++(之所以可以直接ans++,是因为他与割点不同,每条边只访问了一遍) ...

  3. tarjan求割点割边的思考

    这个文章的思路是按照这里来的.这里讨论的都是无向图.应该有向图也差不多. 1.如何求割点 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 根节点如果有两颗及以上子树,它 ...

  4. UVA 796 Critical Links (tarjan算法求割边)

    这是在kuangbin的题目里看到的,不得不吐槽一下,题目中居然没给出数据范围,还是我自己猜的-本来是一道挺裸的题,但是我wa了好多次,原因就是这里面有两个坑点,1重边特判,2输出时左边必须比右边小. ...

  5. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  6. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

  7. (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

    基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...

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

    这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量, ...

  9. 连通分量模板:tarjan: 求割点 &amp;&amp; 桥 &amp;&amp; 缩点 &amp;&amp; 强连通分量 &amp;&amp; 双连通分量 &amp;&amp; LCA(近期公共祖先)

    PS:摘自一不知名的来自大神. 1.割点:若删掉某点后.原连通图分裂为多个子图.则称该点为割点. 2.割点集合:在一个无向连通图中,假设有一个顶点集合,删除这个顶点集合,以及这个集合中全部顶点相关联的 ...

  10. 有向图tarjan算法求连通分量的粗浅讲解、证明, // hdu1269

    打算开始重新复习一遍相关算法.对于有向图tarjan算法,通过学习过很多说法,结合自己的理解,下面给出算法自己的观点. 算法总模型是一个dfs,结合一个stack(存放当前尚未形成SCC的点集合),记 ...

随机推荐

  1. 关于iptables的u32匹配

    前面一篇文章----阐释了iptables最新的bpf match,说它将多个matches并成了一个经过编译的解释型bytecode bpf match,早在bpf match之前,u32 matc ...

  2. typedef和define具体的具体差别

      1) #define是预处理指令,在编译预处理时进行简单的替换,不作正确性检查,不关含义是否正确照样带入,仅仅有在编译已被展开的源程序时才会发现可能的错误并报错.比如: #define PI 3. ...

  3. CorePlot学习

    阅读这篇文章,指出它在国外    原文地址:https://github.com/core-plot/core-plot/wiki/High-Level-Design-Overview 强烈推荐阅读该 ...

  4. If-Modified-Since页面是否更新

    第一次先请求某个网页,抓取到本地,假设文件名为 a.html.这时文件系统有个文件的修改时间. 第二次访问网页,如果发现本地已经有了 a.html,则向服务器发送一个 If-Modified-Sinc ...

  5. [置顶] Cocos2d-x 实例源码分析之二 小实例的主框架

    这篇文章是分析第一个小实例ActionTest的源码.其实所有实例程序的结构都是一样的,只有特定方法里的代码不同,大的框架都是一样的.也就是说看完这篇文章你就可以自己开始分析其他源码了. 废话不多说, ...

  6. [置顶] 深圳华为BSS公共部件 (BI 商业智能 Java Javascript)

    深圳华为BSS公共部件 部门招聘 招聘面试地点:大连,西安 工作地点:深圳 时间:2013年9月7日 联系方式:dawuliang@gmail.com 18675538182 有兴趣的同学,可以直接电 ...

  7. RBAC用户角色权限设计方案

    RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用 户-角色 ...

  8. HP MSA2312 ERROR

    司在用的hp MAS2312存储其中一台每天都会报一个错误 EVENT:Vdisk verification failed. Command failed. (error code: 1) 2 err ...

  9. Java 建立mysql数据库连接的语句

    每次在面试时被问到jdbc的数据路链接过程都卡着,这次不怕了,背会了... 第一个,比较粗糙的 try{   Class.forName("com.mysql.jdbc.Driver&quo ...

  10. achieve aop through xml

    The main way to achive AOP is deploying a xml file. Now a xml file is presented to be a explanation ...