什么是割点:在无向图中,如果去掉一个点以及它的边后,图变得不连通了,那么这个点就是这个无向图的一个割点。
什么是桥:在无向图中,如果去掉一条边使得图不连通了,那么这条边就是无向图的一个桥。
判断割点有两种方法:1、x非root && x有儿子y && low[y]>=dfn[x]
2、x是root && x有>=2个儿子(这里的儿子是基于x的搜索树的,并不是原图上的,也就是说x能到达y并且y在之前没被访问过的时候y才是x的儿子)
判断桥的方法:low[y]>dfn[x]
要注意无向图儿子->父亲的边不作处理,因为如果处理的话,所有low[y]就都等于dfn[x]了
下面是邻接矩阵的写法:
/*判断割点有两种方法:1、x非root && x有儿子y && low[y]>=dfn[x]
2、x是root && x有>=2个儿子(这里的儿子是基于x的搜索树的,并不是原图上的,也就是说x能到达y并且y在之前没被访问过的时候y才是x的儿子)
判断桥的方法:low[y]>dfn[x]
要注意无向图儿子->父亲的边不作处理,因为如果处理的话,所有low[y]就都等于dfn[x]了
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1005;//点的最大数量
int time=1;//过程中用来记录的时间戳
int dfn[N],low[N],fa[N];//dfn记录每个点的时间戳;low记录每个点的追溯值;fa记录每个点的父亲,若无父亲则值为-1
bool g[N][N];//存图
int n,m,a,b;//n是点数,m是边数
void dfs(int x)
{
dfn[x]=low[x]=time++;//先更新时间戳
int child=0;//定义这个变量的目的是为了进行第二种判断割点的方法,这个变量记录的是一个节点搜索树意义上的儿子有几个。
for(int y=1;y<=n;++y)
{
if(g[x][y])//如果x能到y
{
if(!dfn[y])//并且y在之前还没被访问到,那么y就是x的一个搜索树意义下的儿子
{
child++;//搜索树意义上的儿子数量++
fa[y]=x;//记录y的父亲是x,方便之后dfs过程中以y为起点时不让y访问它的父亲x
dfs(y);//以y为起点进行dfs
if(fa[x]==-1&&child>=2)//第二种判断割点的方式
{
cout<<x<<"是割点"<<endl;
}
if(fa[x]!=-1&&low[y]>=dfn[x])//第一种判断割点的方式,这里不用判断有无儿子,因为这个if本来就是有儿子的情况
{
cout<<x<<"是割点"<<endl;
}
if(low[y]>dfn[x])//判断是否是桥
{
cout<<x<<"和"<<y<<"之间的边是桥"<<endl;
}
low[x]=min(low[x],low[y]);//更新x的追溯值,放在上面三个判断前面好像也行,因为判断只用到了x的时间戳= =
}
else if(fa[x]!=y)//如果y不是x的儿子并且y不是x的父亲时,我们可以尝试更新x的追溯值
{
low[x]=min(low[x],dfn[y]);
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;++i)
{
cin>>a>>b;
g[a][b]=1;
g[b][a]=1;//无向图
}
for(int i=1;i<=n;++i)
fa[i]=-1;//先初始化fa数组,如果fa[x]==-1说明x是root,因为那意味着x没父亲
for(int i=1;i<=n;++i)
{
if(!dfn[i])//从没被访问过的点开始dfs
dfs(i);
}
return 0;
}
下面是链式前向星的例题及代码:
注意在某些特殊情况下,割点会重复被判断到,注意去重。(邻接矩阵和链式前向星都要注意这一点)
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
map<int,int> mp;//用来去重
struct edge{
int next;
int to;
int w;
}dat[200005];
int cnt=1;
int head[200005];
void add(int u,int v,int w)
{
dat[cnt].w=w;
dat[cnt].to=v;
dat[cnt].next=head[u];
head[u]=cnt++;
}
const int po = 2e4+5;
int timee=1;
int dfn[po],low[po],fa[po];
int n,m,a,b;
int num=0;
int gd[po];
int gd_i=0;
void dfs(int x)
{
dfn[x]=low[x]=timee++;
int child=0;
for(int i=head[x];i;i=dat[i].next)//遍历和x相连的点
{
int y=dat[i].to;
if(!dfn[y])
{
child++;
fa[y]=x;
dfs(y);
if(fa[x]==-1&&child>=2)
{
if(mp[x]==0)
{
num++;
mp[x]=1;
}
}
if(fa[x]!=-1&&low[y]>=dfn[x])
{
if(mp[x]==0)
{
num++;
mp[x]=1;
}
}
low[x]=min(low[x],low[y]);
}
else if(fa[x]!=y)
{
low[x]=min(low[x],dfn[y]);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;++i)
{
cin>>a>>b;
add(a,b,1);
add(b,a,1);
}
for(int i=1;i<=n;++i)
fa[i]=-1;
for(int i=1;i<=n;++i)
{
if(!dfn[i])
dfs(i);
}
cout<<num<<endl;
for(map<int,int>::iterator j=mp.begin();j!=mp.end();++j)
{
if(j->second!=0)
cout<<j->first<<" ";
}
cout<<endl;
return 0;
}