【图的连通】Tarjan
千古神犇Tarjan,扑通扑通跪下来 sto sto Tarjan orz orz
强联通分量: 思路其实很简单就是dfs跑跑跑,看能不能返祖为一个圈。具体实现并不需要top排序(只是top排序之后可以保证最少次数的从根节点(搜索树根)开始跑tarjan)
void tarjan(int x)
{
dfn[x]=low[x]=++dfsx; sta[++top]=x; inst[x]=1;
int ss=ve[x].size();
for(int i=0;i<ss;i++)
{
if(!dfn[ve[x][i]])
{
tarjan(ve[x][i]);
low[x] = min(low[x],low[ve[x][i]]);
}
else if(inst[ve[x][i]]) low[x] = min(low[x],dfn[ve[x][i]]);
}
int to;
if(low[x]==dfn[x])
{
++scc;
do{
to = sta[top];
inst[to]=0;
blo[to]=scc;
top--;
}while(to!=x);
}
}
以下针对Kosaraju不能解决的众多无向图问题
求割点的无向图tarjan:写法和tarjan差不多,但判low时边不能往父亲判,当一个结点u为割点的条件为对于自己的搜索子结点v,有low[v]>=dfn[u],或者作为根结点,在搜索树(!!!)中有两个或以上的子结点(即从这个点开始可以跑两次tarjan)。 求桥(割边)的无向图tarjan,和求割点差不多,当一条边为树当且仅当对于子结点连的边<u,v>有low[v]>dfn[u]。bool ispoint[];//割点
void tarjan(int x,int fu)
{
dfn[x] = low[x] = ++dfnx;
fa[x] = fu; if(x==1)int child = 0;
int ss=ve[x].size();
for(int i=0;i<ss;i++)
{
if(!dfn[ve[x][i]])
{
if(x==1) child++;
tarjan(ve[x][i],x);
low[x]=min(low[x],low[ve[x][i]]);
if(low[ve[x][i]]<=dfn[x]&&x!=1) ispoint[ve[x][i]]=1;
if(low[ve[x][i]]>dfn[x])
{
printf("from %d to %d is bridge",&x,&ve[x][i]);//桥
}
}
else if(ve[x][i]!=fu)
{
low[x] = min(low[x],dfn[ve[x][i]]);
}
}
if(x!=1) return;
if(child>=2)
{
ispoint[x]=1;
}
}
一些概念:
点双连通分量:不含割点的极大连通子图
点双连通分量很好找呀,在我们徐照每次找到一条边或反向边就把这条边加入栈,当发现割点<u,v>边,我们就一个个出栈,直到边<u,v>,取出来这些点就是一些点双。
割点可以属于多个点双连通分量,其他点和每条边只属于一个点双。
bool ispoint[];//割点
void tarjan(int x,int fu)
{
stack<int>s;
dfn[x] = low[x] = ++dfnx;
fa[x] = fu; if(x==1)int child = 0;
for(int it=la[x];it;it=nt[it])
{
if(!dfn[en[it]])
{
s.push(it);
if(x==1) child++;
tarjan(en[it],x);
low[x]=min(low[x],low[en[it]]);
if(low[en[it]]<=dfn[x])
{
ispoint[x]=1;
bcc++;
while(233)
{
int k=s.top(); s.pop();
int x=st[k]; int y=en[k];
if(bl[x]!=bcc)
{
blo[bcc].push_back(x); bl[x]=bcc;
}
if(bl[y]!=bcc)
{
blo[bcc].push_back(y); bl[y]=bcc;
}
if(k==it) break;
}
}
}
else if(en[it]!=fu)
{
s.push(it);
low[x] = min(low[x],dfn[ve[x][i]]);
}
}
if(x!=1) return;
if(child<2)
{
ispoint[x]=0;
}
}
边双连通分量:不含割边的极大连通子图
将所有的边求出了后,直接将所有的割边全部删除,然后原本的图就分成了若干个连通的块块,那么对于每个块就是一个边双。
桥不属于任何边双,对于其他的点和边只属于一个边双。
代码这个就不给了owo。

浙公网安备 33010602011771号