GRL_3_A:Articulation Points

题目链接

题目给出了一个无向有权连通图。

定义关节点的概念:如果在一个连通图中,去掉某一个顶点和从这个顶点引出的所有边之后,剩下的其他节点不在同一个连通分量里,则称这个节点为关节点。

题目给出一个图之后,要按顺序输出本图中的关节点。

思路:

基本思路是对dfs进行优化,在dfs的过程中定义一些数据:

按照dfs的过程生成一个dfs tree记为T;

pre[u] 用来存储u节点在dfs中的顺序;

parent[u] 用来存储在T树中,点u的父亲节点;

low[u]表示的的含义是,与 点u及其所有后代节点 相连接的所有节点中,pre值最小的

个节点x的pre[x]记为u点的low[u];

我们可以想到,通过这个low的值,我们可以判断出关节点,因为对于某一点x来说,如果x点的子节点的low值比x点的pre值更小,就说明在x本身或者其所有的子节点中一定存在某一条路径,连接到x点在T树中的祖先。那么通过这条路径,就一定可以把x下面的子树与x的祖先相连,保证这个点不是关节点;反之,也能保证x点就是关节点。

所以:对于各顶点u,设其父节点为p,如果 pre[p]<=low[u] ,那么p必定是关节点

同时,对于根节点而言,由于根节点没有祖先,没有父亲节点,所以如果根节点有2个或以上的子节点,那么这个根节点就一定是关节点。

pre和parent的更新都可以和dfs同时完成。

关键在于low的值如何更新,可以想到,对于某一节点,其low的值不仅取决于其dfs的顺序,还取决于他的所有后代节点中的最小的low值。所以,最好要从dfs tree的底部开始更新low值,这样dfs结束的时候low值的更新就完成了。

我们这样来更新low[u]的值:

1.pre[u]

2.如果存在一条边,连接点u和u在T中的某个祖先节点x,祖先节点的pre[x]

3.u节点的所有子节点中的low值最小的节点y,y的low[y]

选取上面三个值中的较小值来作为low的值,即:

$$ low[u]=min\{pre[u],pre[x],low[y]\} $$

最后通过下面两个条件来判断

1.点u是根节点,如果u有2个或以上子节点,则u为关节点【充要条件】

2.点u不是根节点,设p=parent[u],如果pre[p]<=low[u],那么p为关节点【充要条件】

 

具体代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int maxx=100010;

vector<int> A[maxx];
int pre[maxx],parent[maxx],low[maxx];
bool flag[maxx];
int v,e,now=1;

void dfs(int u){
	flag[u]=true;
	low[u]=now;
	pre[u]=now;
	now++;
	for(int i=0;i<A[u].size();i++){
		if(flag[A[u][i]]==false) {//if the next has not been visited
			parent[A[u][i]]=u;//update the parent array
			dfs(A[u][i]);//dfs the next point
			low[u]=min(low[u],low[A[u][i]]);//if the low value of son point is smaller
		}
		else if(parent[u]!=A[u][i]){//if a point has been visited and the edge between u and A[u][i] is not in dfs tree,
			low[u]=min(low[u],pre[A[u][i]]);//update the value of low array
		}
		
	}
	return ;
}


int main (){
	int x,y;
	parent[0]=0;
	for(int i=0;i<maxx;i++){
		flag[i]=false;
	}
	cin>>v>>e;
	while(e--){
		cin>>x>>y;
		A[x].push_back(y);
		A[y].push_back(x);
	}
	dfs(0);
	
	//for(int i=0;i<v;i++) cout<<i<<"  "<<low[i]<<endl;//debug
	
	set<int> ans;//save the answer data in set
	int cnt=0;
	for(int i=1;i<v;i++){
		int p=parent[i];
		if(p==0) cnt++;
		else if(pre[p]<=low[i]) ans.insert(p);
	}
	if(cnt>=2) ans.insert(0);
	for(set<int>::iterator it=ans.begin();it!=ans.end();it++) cout<<*it<<endl;
	return 0;
	
}

 

错点:

 

1.无向图用邻接矩阵存的时候要双向存;

2.在dfs的过程中,想要从上往下执行,应该把代码块放在dfs的递归前面,要从下往上执行,应该放在dfs递归的后面;

3.因为关节点是可能有多个子节点,所以答案的存储应该存储在set里,来去重并且排序;

4.迭代器遍历时,it!=ans.end(),不能写<=;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值