/*
经过简单的使用,对刘汝佳的这个板做几个简单的说明:
1、算法结束后每个bcc[i]里存入一个双连通分量的点的标号,其中割点也被存进去,也就是说割点可以属于多个双连通分量
2、根据刘汝佳所说的点—双连通的要求,“任意两条边都在一个简单环中”,但是从代码中看到只要找到割点就要开始组一个连通分量了,
那么问题就来了,如果割点的两侧都不是环呢?
比如对一棵树来说,任一点都是割点,点的两侧都是一条边,没有环,那怎么算他的双连通分量呢?
根据调试跟踪的结果,对一棵树来说一条边所关联的两点就是一个点双连通分量,也就是说,两个相邻的割点也各自被认作了一个双连通分量
3、割点的bccno没有意义,因为割点属于多个双连通分量
4、蓝书上认为没必要清空stack S,保险起见我给加上了
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
const int maxn = 10000 + 5;
struct Edge
{
int u, v;
};
int iscut[maxn], pre[maxn], bccno[maxn], dfs_clock, bcc_cnt;
vector<int>G[maxn], bcc[maxn];
stack<Edge>S;
int dfs(int u, int fa)
{
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for (int i = 0; i<G[u].size(); i++){
int v = G[u][i];
Edge e = { u, v };
if (!pre[v]){
S.push(e);
child++;
int lowv = dfs(v, u);
lowu = min(lowv, lowu);
if (lowv >= pre[u]){
iscut[u] = 1;
bcc_cnt++; bcc[bcc_cnt].clear();
for (;;){
Edge x = S.top(); S.pop();
if (bccno[x.u] != bcc_cnt){
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if (bccno[x.v] != bcc_cnt){
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
/*造成上述第二条的一个重要原因就是这个判断放在了上面两个if语句之下,所以这条跳出边的两端点已被加入了bcc[bcc_cnt]中;
另外,这条跳出的边是bcc内部的边,不是外部的边;
*/
if (x.u == u &&x.v == v){
break;
}
}
}
}
else if (pre[v]<pre[u] && v != fa){
S.push(e);
lowu = min(lowu, pre[v]);
}
}
if (fa < 0 && child == 1) iscut[u] = 0;
/*这里可能会有一个疑问,既然根节点有可能不是割点,根节点所在的点联通分量岂不是会被漏掉了?
其实,因为根节点满足lowv >= pre[root],所以在上面的判断中已经被视为一个割点,因此将他所在的连通分量作为一个点连通分量存储起来了,
并且将其所关联得边从栈中清空,因此不会发生漏掉根节点所在连通分量的情况
*/
return lowu;
}
void find_bcc(int n)
{
mem(pre, 0);
mem(bccno, 0);
mem(iscut, 0);
while (!S.empty()) S.pop();
dfs_clock = bcc_cnt = 0;
for (int i = 1; i <= n; i++)
if (!pre[i]) dfs(i, -1);
}
对刘汝佳蓝书点双连通分量(BCC)模板的理解
最新推荐文章于 2021-07-16 12:16:35 发布