二、无向图的割点与桥
什么是无向图?简单来说,若一个图中每条边都是无方向的,则称为无向图。
割点
若从图中删除节点 x 以及所有与 x 关联的边之后,图将被分成两个或两个以上的不相连的子图,那么称 x 为图的割点。
桥
若从图中删除边 e 之后,图将分裂成两个不相连的子图,那么称 e 为图的桥或割边。
三、如何求解图的割点与桥?
在了解了 Tarjan 算法的背景以及图的割点与桥的基本概念之后,我们下面所面临的问题就是 —— 如何求解图的割点与桥?
开门见山,我们直接引出 Tarjan 算法在求解无向图的割点与桥的工作原理。
时间戳
时间戳是用来标记图中每个节点在进行深度优先搜索时被访问的时间顺序,当然,你可以理解成一个序号(这个序号由小到大),用 dfn[x] 来表示。
搜索树
在无向图中,我们以某一个节点 x 出发进行深度优先搜索,每一个节点只访问一次,所有被访问过的节点与边构成一棵树,我们可以称之为“无向连通图的搜索树”。
追溯值
追溯值用来表示从当前节点 x 作为搜索树的根节点出发,能够访问到的所有节点中,时间戳最小的值 —— low[x]。那么,我们要限定下什么是“能够访问到的所有节点”?,其需要满足下面的条件之一即可:
- 以 x 为根的搜索树的所有节点
- 通过一条非搜索树上的边,能够到达搜索树的所有节点
为了方便理解,让我们通过动画的方式来模拟追溯值真实计算过程。
……
设 \(subtree(x)\) 表示搜索树中以 \(x\) 为根的子树。low[x]
定义为以下节点的时间戳的最小值:
- \(subtree(x)\) 中的节点
- 通过 1 条不在搜索树上的边,能够到达 \(subtree(x)\) 的节点
若搜索树上 \(x\) 是 \(y\) 的父节点,则令 low[x]=min(low[x],low[y])
。
若无向边 \((x,y)\) 不是搜索树上的边,则令 low[x]=min(low[x],dfn[y])
。
割边判定法则
无向边 \((x,y)\) 是桥,当且仅当搜索树上存在 \(x\) 的一个节点 \(y\),满足:
\[dfn[x]<low[y] \]void tarjan(int x,int in_edge){
dfn[x]=low[x]=++cnt;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(!dfn[u]){
tarjan(u,i);
low[x]=min(low[x],low[u]);
if(low[u]>dfn[x])
bridge[i]=bridge[i^1]=true;
}
else if(i!=(in_egde^1))
low[x]=min(low[x],dfn[u]);
}
}
割点判定法则
若 \(x\) 不是根节点,则 \(x\) 是割点当且仅当搜索树上存在 \(x\) 的一个节点 \(y\),满足:
\[dfn[x] \le low[y] \]特别地,如果 \(x\) 是搜索树的根节点,则 \(x\) 是割点当且仅当搜索树上存在 至少两个子节点 \(y_1,y_2\),满足上述条件
void tarjan(int x){
dfn[x]=low[x]=++cnt;
int flag=0;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(!dfn[u]){
tarjan(u);
low[x]=min(low[x],low[u]);
if(low[u]>=dfn[x]){
flag++;
if(x!=root||flag>1) cut[x]=true;
}
}
else low[x]=min(low[x],dfn[u]);
}
}
标签:Tarjan,int,割点,算法,dfn,搜索,low,节点
From: https://www.cnblogs.com/ycw123/p/16757543.html