无向图连通度(割)

无向图连通度(割) – (参考的一个acm模板,不知道挂啥链接)

算法思想

观察 dfs 搜索树,我们可以发现有两类节点可以成为割点:

  1. 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;

  2. 对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。

  3. 设置vis, pre, anc数组,cnt记录dfs树中的孩子个数。

    • pre[cur]:cur结点dfs时访问时的时间’。
    • anc[cur]: 记录cur结点及其子孙结点最早祖先的dfs时间(朔源到尽头)。
    • vis标记结点访问状态:0:未访问, 1:正在访问, 2:访问结束。
  4. 当(u,v)为树边且 anc[v] >= dfn[u] 时,节点u才为割点。该式子的含义:以节点v为根的子树所能追溯到最早的祖先节点要么为v要么为u,注意 u 必须不是根结点。

  5. 题目链接

邻接矩阵版
/*==================================================*\
 | 无向图连通度(割) 
 | INIT: edge[][]邻接矩阵;vis[],pre[],anc[],deg[]置为0;
 | CALL: dfs(0, -1, 1, n); 
 | k=deg[0], deg[i]+1(i=1…n-1)为删除该节点后得到的连通图个数
 | 注意:0作为根比较特殊! 
\*==================================================*/
int edge[V][V], anc[V], pre[V], vis[V]; 
vector<vector<int> > ans;
void dfs(int cur, int father, int dep, int n){ 			// vertex: 0 ~ n-1
	vis[cur] = 1;		//开始访问
	pre[cur] = anc[cur] = dep; 
	int cnt  = 0;
	for(int i = 0; i < n; ++i) 
		if(edge[cur][i]){ 
 			if(i != father && 1 == vis[i]){ 	//i访问还未结束,所以为cur的祖先
 				if(pre[i] < anc[cur]) 			//更新cur的最早祖先
					anc[cur] = pre[i];			//back edge
 			} 
 			if(0 == vis[i]){ 					//tree edge,i还未访问,搜索树的树边
				dfs(i, cur, dep+1, n); 
				cnt++;
 				if(anc[i] < anc[cur])			//如果cur的最小祖先大于i的最小祖先,且存在cur->i,则更新
		 			anc[cur] = anc[i]; 			//cur的最小祖先
	 			if((cur == 0 && cnt > 1) && (cur != 0 && anc[i] >= pre[cur]))			//如果cur的搜索时间比child的最早的祖先时间还小或者相等,则找到一个割点(child及其子孙最早也只能溯源到cur), 注意cur!=根节点
					deg[cur]++;
			} 
 		} 
 	vis[cur] = 2; 		//结束访问
} 
邻接表版
int anc[V], pre[V], vis[V], deg[V]; 
vector<int> adj[V];
void dfs(int cur, int father, int dep, int n){ 			// vertex: 0 ~ n-1 
	vis[cur] = 1;		//开始访问
	pre[cur] = anc[cur] = dep; 
    int cnt = 0;
	for(int i = 0; i < adj[cur].size(); ++i){ 
		int child = adj[cur][i];
 		if(child != father && 1 == vis[child]){ 	//child访问还未结束,所以为cur的祖先
 			if(pre[child] < anc[cur]) 				//更新cur的最早祖先
				anc[cur] = pre[child];			//back edge
 		} 
 		if(0 == vis[child]){ 					//tree edge, child还未访问,搜索树的树边
			dfs(child, cur, dep+1, n); 
			cnt++;
 			if(anc[child] < anc[cur])			//如果cur的最小祖先大于child的最小祖先,且存在cur->child,
		 		anc[cur] = anc[child]; 			//则更新cur的最小祖先
	 		if((cur == 0 && cnt > 1) || (cur != 0 && anc[child] >= pre[cur]))	//如果cur的搜索时间比child的最早的祖先时间还小或者相等,则找到一个割点(child及其子孙最早也只能溯源到cur), 注意cur!=根节点
				deg[cur]++;
		} 
 	} 
 	vis[cur] = 2; 		//结束访问
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值