知识点 - 割点与割边
概念(一)割点
针对无向连通图,若删除一个点后使得该图不连通,则该点是割点。
注意:一个图中可能有多个割点
复杂度:
O ( N + M ) O(N+M) O(N+M)
实现
我们在遍历所有点时会遇到割点(废话),主要是如何认定一个点是割点。假设访问到了k点,如果在没有访问过的点中,至少有一个点在不经过k点的情况下,无法回到已访问过的点,则k点是割点。(因为该图删除点k后不连通了)
算法核心: 如何判断未被访问过的点u在不经过点k的情况下能否返回任何一个已访问过的点。
从树的角度来看,k是u的父亲,u是k的儿子,判断u能否不经过k而回到它的所有祖先。
我们用数组low来表示每个点在不经过父节点的前提下,能返回的最早的时间戳。
#include <bits/stdc++.h>
using namespace std;
int n,m,e[1005][1005],root,num[1005],low[1005],flag[1005],index;
void dfs(int cur,int dad) {
int i,child=0;
index++;
num[cur]=index;
low[cur]=index;
for(i=1; i<=n; i++) {
if(e[cur][i]==1) {
if(num[i]==0) {
child++;
dfs(i,cur);
low[cur]=min(low[cur],low[i]);
if(cur!=root && low[i]>=num[cur]) {
flag[cur]=1;
} else if(cur==root && child==2) {
flag[cur]=1;
}
} else if(low[i]!=dad) {
low[cur]=min(low[cur],num[i]);
}
}
}
}
int main() {
int i,a,b,c;
scanf("%d%d",&n,&m);
memset(e,0,sizeof(e));
for(i=1; i<=m; i++) {
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
root=1;
dfs(1,root);
return 0;
}
概念(二)割边
割边也成为桥,与割点类似
针对无向连通图,若删除一条边后使得该图不连通,则该边是割边。
同样的,一个图中可能有多条割边
割边的求法也与割点类似,只需将
if(low[i]>=num[cur])
改为:
if(low[i]>num[cur])
就行了,即该点到除了达不了它的任何祖先,也无法到达它的父亲。
好了,呈上代码,请注意输出部分:
#include<bits/stdc++.h>
using namespace std;
int n,m,e[1005][1005],num[1005],low[1005];
int root,index;
void dfs(int cur,int dad) { //待判断顶点和它的父节点
int i;
index++;//index储存的是时间戳
num[cur]=low[cur]=index;
for(i=1; i<=n; i++) {
if(e[cur][i]==1) {
if(num[i]==0) { //i是cur的儿子
dfs(i,cur);//继续遍历
low[cur]=min(low[cur],low[i]);//更新low的值
if(low[i]>num[cur])
printf("%d-%d\n",cur,i);
} else if(i!=dad) //i是cur的祖先,更新low
low[cur]=min(low[cur],num[i]);
}
}
}
int main() {
int i,a,b;
scanf("%d%d",&n,&m);
for(i=1; i<=m; i++) { //注意是无向图哦
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
root=1;
dfs(1,root);
return 0;
}