题目给出了一个无向有权连通图。
定义关节点的概念:如果在一个连通图中,去掉某一个顶点和从这个顶点引出的所有边之后,剩下的其他节点不在同一个连通分量里,则称这个节点为关节点。
题目给出一个图之后,要按顺序输出本图中的关节点。
思路:
基本思路是对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(),不能写<=;