题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1269

以下内容为原创,转载请声明。

强连通分量SCC(Strongly Connected Component):一个图的子图,如果任何两个点都互相可达且满足最大性,该子图就是原图的SCC。

对于有向图的连通性,tarjan可以说是十分牛逼了,由于tarjan只需要一次dfs就能判断有向图中所有的SCC,所以他的复杂度是O(|V|+|E|),也就是把每条边和每个点都会检索一遍,是在线性时间能处理出所有的SCC的!下面简单说明求强连通分量的tarjan算法的过程以及正确性。

基础知识:

①、经dfs处理过的点的low值相同的一定属于同一个SCC,这个SCC中第一个被dfs访问的点也就是dfn=low的点一定就是这个SCC中每个点的low值,显而易见。

②、在dfs过程中,dfs进行最深处一定会进入某一个SCC,(尽管在过程中可能会进入其他SCC),下面我会解释

③、标记SCC Number的过程可以用栈实现,因为dfs的过程就是递归,递归和栈非常相似

tarjan算法的流程:将每个访问的点入栈,更新low值,直到到达一个点,这个点low值等于dfn值,说明这个点一定是一个SCC的祖先,也就是第一个访问的点,此时这个点一定在栈的底部!!因为是通过dfs退出多次而不弹栈才得到这个low=dfn。我们只要不断弹出l这个SCC的点并标记就可以,直到弹到栈底。我们先看基础知识(2),上图中在访问3的时候进入了4、5结点,也就是其他SCC,到了5的时候,由于low[5]=dfn[5]所以此时弹栈,发现就只有一个5属于这个SCC,4也是同样的过程,直到到了3,进入另一个分支6,这就是我基础知识(2)中的一定会在最终进入一个SCC而且栈底元素就是这个SCC的祖结点。

关于强连通分量处理回退边的方式,我的解释是这样的:

当一个点已经标记过dfn之后,而且这个点在之前没有标记过SCC(因为在dfs过程中会进入并处理其他SCC,若此时有边相连就无需更新,因为回退边所到点的low值一定小于该点的low)就用

①、low[u]=min(low[v],dfn[v])  ②、low[u]=min(low[v],low[u])

来更新low值,其实对于双连通分量来说,这两种更新方式的结果是一样的,但是第一种方法在原理上是错误的!!!!! 我来举一个例子,又是下面这张图,

对于第一种我们得出的结果是下面第一张图,而对于第二张我们得到的结果是下面第二张图,我们可以发现,根据tarjan算法的思想,处于同一个强连通分量中的点low值应该是相同的,但是第一种做法中的点的low值是不同的,但是他们得到的结果都是一样的,都是图是强连通的,为什么呢?

 下面我将证明第一种做法得出正确结果的原因:

由于对于4,5号节点来说,他们的low值为3的时候,都与他们的dfn值不相同(dfn[4]=4&dfn[5]=5),所以他们就会一直在栈中,而栈底也一定是1号结点。4,5号结点没有机会弹出,直到dfs return到1号结点,此时才会弹栈,一直到弹出1号结点为止,这就把栈中的结点4,5弹出了,但是事实上他们的low值是更新错误的。尽管是错误的,但是中途dfs在访问其他SCC的时候(如果有的话,可以参考最上面那张图)已经把其他SCC的点都标记了,此时栈中剩下的只有这一个SCC!!所以最终的结果是相同的,但是!!这种更新方法是错误的!!因为在之后不能通过他们的不同的low值数量去查看SCC的数量,只能从每一次弹栈中给出的SCCNUMBER中得到SCC的信息。而且,这种做法跟tarjan的算法中“每个SCC”的low值是相同的是矛盾的

看完我的文章能不能支持一下呢

hdu1269代码如下:

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
#define pf printf
#define mem(a,b) memset(a,b,sizeof(a))
#define prime1 1e9+7
#define prime2 1e9+9
#define pi 3.14159265
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define scand(x) scanf("%llf",&x)
#define f(i,a,b) for(int i=a;i<=b;i++)
#define scan(a) scanf("%d",&a)
#define mp(a,b) make_pair((a),(b))
#define P pair<int,int>
#define dbg(args) cout<<#args<<":"<<args<<endl;
#define inf 0x3f3f3f3f
inline int read(){
int ans=,w=;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-;ch=getchar();}
while(isdigit(ch))ans=(ans<<)+(ans<<)+ch-'',ch=getchar();
return ans*w;
}
const int maxn=1e5+;
int n,m,t;
int head[maxn],nxt[maxn],low[maxn],sccno[maxn],dfn[maxn],id;
struct node{
int u,v;
}p[maxn*];
int e=;
int cnt;
stack<int> sk;
void addedge(int u,int v)
{
p[e].u=u;
p[e].v=v;
nxt[e]=head[u];
head[u]=e++;
}
void tarjan(int u)
{
low[u]=dfn[u]=++id;
sk.push(u);//将结点编号压栈
for(int i=head[u];~i;i=nxt[i])
{
int v=p[i].v;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!sccno[v])//回退边且边终点没有标记SCC
low[u]=min(low[u],low[v]);
}
if(low[u]==dfn[u])
{
cnt++;//连通分量的数量增加,对每个点这个if语句只执行一次
while()
{
int v=sk.top();
sk.pop();
sccno[v]=cnt;
if(u==v)break;
}
}
}
int main()
{
freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
std::ios::sync_with_stdio(false);
while(scanf("%d%d",&n,&m)==)
{
if(n==&&m==)break;
e=;cnt=;
mem(head,-);mem(nxt,-);
while(!sk.empty())sk.pop();
int x,y;
f(i,,m)
{
x=read();
y=read();
addedge(x,y);
}
mem(dfn,);
id=;
mem(sccno,);
mem(low,);
f(i,,n)
{
if(!dfn[i])
tarjan(i);
}
f(i,,n)dbg(low[i]);
if(cnt==)pf("Yes\n");
else pf("No\n");
}
}

tarjan算法强连通分量的正确性解释+错误更新方法的解释!!!+hdu1269的更多相关文章

  1. Tarjan算法--强连通分量

    tarjan的过程就是dfs过程. 图一般能画成树,树的边有三种类型,树枝边 + 横叉边(两点没有父子关系) + 后向边(两点之间有父子关系): 可以看到只有后向边能构成环,即只有第三张图是强连通分量 ...

  2. Tarjan求强连通分量,缩点,割点

    Tarjan算法是由美国著名计算机专家发明的,其主要特点就是可以求强连通分量和缩点·割点. 而强联通分量便是在一个图中如果有一个子图,且这个子图中所有的点都可以相互到达,这个子图便是一个强连通分量,并 ...

  3. 【学习整理】Tarjan:强连通分量+割点+割边

    Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 ...

  4. tarjan求强连通分量+缩点+割点以及一些证明

    “tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往”----<膜你抄>   自从听完这首歌,我就对tarjan开始心驰神往了,不过由于之前水平不足,一 ...

  5. tarjan求强连通分量+缩点+割点/割桥(点双/边双)以及一些证明

    “tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往”----<膜你抄>   自从听完这首歌,我就对tarjan开始心驰神往了,不过由于之前水平不足,一 ...

  6. CCF 高速公路 tarjan求强连通分量

    问题描述 某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路. 现在,大臣们帮国王拟了一个修高速公路的 ...

  7. Tarjan求强连通分量、求桥和割点模板

    Tarjan 求强连通分量模板.参考博客 #include<stdio.h> #include<stack> #include<algorithm> using n ...

  8. UESTC 901 方老师抢银行 --Tarjan求强连通分量

    思路:如果出现了一个强连通分量,那么走到这个点时一定会在强连通分量里的点全部走一遍,这样才能更大.所以我们首先用Tarjan跑一遍求出所有强连通分量,然后将强连通分量缩成点(用到栈)然后就变成了一个D ...

  9. HDU 1827 Summer Holiday(tarjan求强连通分量+缩点构成新图+统计入度+一点贪心思)经典缩点入门题

    Summer Holiday Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

随机推荐

  1. Centos7上查看ext4文件系统的实际创建时间

    前提:今日查看nginx日志时发现有报错,说是一些js,css文件找不到,于是想到去实际路径下查看文件是否确实不存在.结果出现下图中报错: 经过别人提醒查看文件的时间,于是看了一下登上服务器是9:52 ...

  2. Dubbo之心跳机制 · 房东的小黑

    在网络传输中,怎么确保通道连接的可用性是一个很重要的问题,简单的说,在网络通信中有客户端和服务端,一个负责发送请求,一个负责接收请求,在保证连接有效性的背景下,这两个物体扮演了什么角色,心跳机制能有效 ...

  3. 学习HEXO的历程

    前言: 简介 开始搭建 命令 API测试 逛github相关的帖子时,发现了hexo.正好想要做一个个人的博客,用来记录自己的各类感悟,所以花一些时间学习学习,以后博客可以放github,省得去注册c ...

  4. 一个异步访问redis的内存问题

    | 分类 redis  | 遇到一个redis实例突然内存飙高的案例, 具体症状如下: 客户端使用异步访问模式 单个请求的回包很大,hgetall一个8M的key 由于访问量比较大,已经登录不上red ...

  5. Angular总结

    angular关键核心点进行总结 1 2 angular中有很多知识点需要学习,学习成本是很大的,我通过平常开发中把一些 很重要知识点总结下来,不管是以后拿来用,或者跳槽面试需要,我都感觉是很有帮助的 ...

  6. Mybatis调用存储过程报错

    Mybatis调用存储过程 贴码 123456 Error querying database. Cause: java.sql.SQLException: User does not have ac ...

  7. 7-11 jmu-python-分段函数&数学函数 (15 分)

    本题要求计算下列分段函数f(x)的值(x为从键盘输入的一个任意实数): 输入格式: 直接输入一个实数x 输出格式: 在一行中按“f(x)=result”的格式输出,其中x与result都保留三位小数. ...

  8. 正式学习MVC 02

    1.cookie 继续讲解MVC的内置对象cookie 相对不安全 1)保存cookie public ActionResult Index() { // 设置cookie以及过期时间 Respons ...

  9. 对HTML语义化的一些理解和记录

    什么是HTML语义化 说HTML语义化就要先说说HTML到底负责的什么?下面摘自维基百科: 超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页 ...

  10. 大厂常问iOS面试题--性能优化篇

    1.造成tableView卡顿的原因有哪些? 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候 ...