对刘汝佳蓝书点双连通分量(BCC)模板的理解

/*
	经过简单的使用,对刘汝佳的这个板做几个简单的说明:
	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);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值