无向图
相关概念:割点、桥、点、(点/边)双连通分量。
- 若边 为桥,则 ,注意父亲来的边
void tarjan(int x,int E) {
dfn[x]=low[x]=++num;
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(!dfn[y]) {
tarjan(y,k^1);
low[x]=min(low[x],low[y]);
if(dfn[x]<low[y])
bridge[k]=bridge[k^1]=1;
}
else if(k!=E) low[x]=min(low[x],dfn[y]);
}
}
- 若 为割点,则 。特别的,对于根节点,应该有两个以上这样的子节点。
void tarjan(int x) {
low[x]=dfn[x]=++num;
int flag=0;
for(int k=last[x];k;k=a[k].next) {
int y=a[k].y;
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y]) {
flag++;
if(x!=root || flag>1) cut[x]=1;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
- 边双连通分量的求法:把所有桥标记,(桥数+原连通块个数=点双连通分量个数)不走桥来标记各个连通块,就找出了所有 .用桥边连接各个 ,从而实现缩点。
- 点双连通分量的求法:1.第一次访问节点时,让它入栈。2.当出现
时,弹出节点,直至
被弹出。弹出的点和
构成一个
.
点双连通分量的缩点:用割点连接各个 .
点双连通分量的求法的正确性证明:
根据栈先进后出的性质,对于一个节点 ,它在 时会被弹出——显然这构成一个简单环。
如何理解呢?
有向图
用一个栈存有后向边到达当前点祖先的点.
这些点一定是对求环有用的.
所以可以得到如下算法:
void tarjan(int x) {
dfn[x]=low[x]=++num;
v[sta[++top]=x]=1;
for(int k=last[x],y;k;k=a[k].next) {
y=a[k].y;
if(!dfn[y]) {
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(v[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]) {
int y; ++cnt;
do {
v[y=sta[top--]]=0;
dcc[y]=cnt;
} while(y^x);
}
}