您的位置:首页 > 其它

强连通分量——tarjan ->缩点

2017-02-24 11:49 337 查看
对于一些题目,我们找出强连通分量后,就会变得非常简单=v=+

首先介绍强连通:

对于一个有向图,每一对点(x,y)都可以相互到达,则称之为强连通图。

而一个有向图中的极大强连通子图,就称为强连通分量

(注:极大的意思就是说不能再往这个子图中添加点,即当前情况下的最大子图

(强连通都是:环,环套环,环套环套环……

接下来就是求出强连通分量:这里只介绍tarjan算法

定义时间戳dfn[i]表示i是第几个被访问到的节点。

定义返祖数组low[i]表示本块强连通分量中最早被访问的节点的时间

进行深搜,把路径上所有的点压入一个栈中。

那么什么时候弹出?

当一个强连通分量中所有点都被遍历的时候。一个最显而易见的条件:回溯到强连通分量最早被访问的节点。

根据low和dfn的定义,我们可以知道当low[x]==dfn[x]时,我们得到了一个包含x的强连通分量,此时就可以把x到栈顶的所有元素弹出并记录。

那么low[x]如何更新?

① 子节点回溯得到

② 访问到一个已被访问且未被弹出的点

tarjan-dfs部分

inline void dfs(int x)
{
dfn[x]=low[x]=++index;
sta[++top]=x;
ins[x]=1;
for(int i=f[x];i;i=next[i])
{
int t=poi[i];
if(!dfn[t])    {    dfs(t);low[x]=min(low[x],low[t]);}
else
if(ins[t])
low[x]=min(low[x],low[t]);
}
if(dfn[x]==low[x])
{
while(1)
{
cnt++;
int t=sta[top];
top--;
ins[t]=0;
lin[t]=cnt;
XXXXXXXXX(依题目决定)
if(t==x)    break;
}
}
}


所谓tarjan缩点,就是把整个强连通分量当作一个点来进行处理。

显然得到一个性质,缩点后的图不存在环。

下面以 bzoj1051/luogu2341 HAOI2006 受欢迎的牛为例

传送门

这道题的大意就是说有多少头牛被其他所有的牛喜欢,喜欢具有传递性。

乍一看。。。floyd传递闭包?

再一看数据- -

行吧,算我输

经过观察可以发现,我们如果把他们的关系绘成图有以下性质

① 一个强连通子图中所有奶牛都相互喜欢

② 若强连通子图A中有奶牛喜欢强连通子图B中的奶牛,则所有A中奶牛都喜欢所有B中奶牛

那么仔细思考一下,如果我们通过tarjan缩点,把整个图变成有向无环图,是不是就成了一道水题?

因为无环,所有要被所有奶牛崇拜,则这个强连通分量不会有边连到其他的强连通分量,否则就会构成环,即出度为0。

朴素的想法,如果一个强连通分量的入度=分量总数-1,并且出度=0,则满足条件。

但是如果动下脑子,,就可以想到一个巧妙的办法:

当出度为0的点>1个,则 入度=分量总数-1 条件绝对不成立

答案为0

如果出度为0的点=0个 则 出度=0 条件绝对不成立

答案为0

那么显然,如果答案要不为0,出度为0的点=1个!

答案即为这个点的权值(即缩成这个点的强连通分量所包含节点的数量)

CODE:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
bool ins[10001];
int index=0,top=0,cnt=0,n,m;
int sta[10001],dfn[10001],low[10001],poi[100001],next[100001],f[10001],lin[10001],x[50001],y[50001],num[10001],vis[10001],cant[10001];
inline void add(int x,int y)
{
poi[++cnt]=y;next[cnt]=f[x];f[x]=cnt;
}
inline void dfs(int x)
{
dfn[x]=low[x]=++index;
sta[++top]=x;
ins[x]=1;
for(int i=f[x];i;i=next[i])
{
int t=poi[i];
if(!dfn[t])    {    dfs(t);low[x]=min(low[x],low[t]);}
else
if(ins[x])
low[x]=min(low[x],low[t]);
}
if(dfn[x]==low[x])
{
while(1)
{
int t=sta[top];
top--;
ins[t]=0;
lin[t]=x;
num[x]++;
vis[x]=1;
if(t==x)    break;
}
}
}
inline void out()
{
for(int i=1;i<=m;i++)
if(lin[x[i]]!=lin[y[i]])    cant[lin[x[i]]]=1;
int hh=0,tx=-1;
for(int i=1;i<=n;i++)
if(!cant[i]&&vis[i])
{
tx=i;
hh++;
if(hh==2)
{
printf("0\n");
return;
}
}
if(tx==-1)    {printf("0\n");return;}
printf("%d\n",num[tx]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])    dfs(i);
}
out();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图论 强连通分量