◇◆◇概念◇◆◇
联通块:任意两个点之间存在至少一条路径可以到达对方的一个点集。
说白了就是某一块里面的点是连起来的。
割边:割掉某一条边能使整个图分为两个联通块的边,又称关键边(桥)。
割点:割掉某一个点(这意味着失去所有连向这个点的边)使整个图分为两个联通块的点。
◇◆◇目标◇◆◇
找出一个无向图中的割点、割边
◇◆◇怎么找◇◆◇
先在这里插入两个概念。
在我们DFS的时候,假设我们现在在结点u,u有一条边通向v.
树边:如果v没有被访问过,那么DFS(v),边(u,v)为一条树边。
回边(反祖边):如果v被访问过,那么说明v的编号比u大,边(u,v)为一条反祖边。
①确定遍历图的方式:DFS深度优先搜索
目的:将图变成一颗DFS树
②利用DFS对遍历到的点进行编号
用dfn[i]来表示第i号结点是第几个被遍历到的。
③!对于每一个点u,定义low[]。
low[u]为u通过它的后代结点能访问到的最小的dfn(就是在u上面的结点)。
这里指通过多条树边,一条返祖边可以到达的最小的dfn(最上面的结点)。
④割边:对于每一个点u,如果low[v]小于等于dfn[u]说明v可以通过v的后代通到u的上面,那么割掉u没有用。
如果low[v]大于dfn[u],那么割掉(u,v)后u和v一定处于两个联通块中。
如图:
⑤割点:也是同样的道理,但是当low[v]等于dfn[u]时u已经被删掉了,所以这种方案是割点。判断条件:low[v]>=dfn[u]
◇◆◇某些不可描述的细节◇◆◇
①根节点:它没有父亲,只要它有两个儿子就可以作为割点了。
②
③
https://vjudge.net/problem/HihoCoder-1183
代码(copy真的不好):
#include<set>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define MAXN 20000
void G();
struct edge { //结构体数组表示边
int u,v;
};
bool operator<(edge a,edge b){ //给set定义优先级
if(a.u==b.u) return a.v<b.v;
return a.u<b.u;
}
//set具有判重功能,避免重复的点、边
set<int>Point; //割点
set<edge>Bridge; //割边
vector<int>G[MAXN+5]; //vector存入边
int n,m,cnt;
int fa[MAXN+5];
int dfn[MAXN+5],low[MAXN+5];
void G() {printf("GoodGame\n");}
void Dfs(int u) {
int Children=0;
dfn[u]=low[u]=++cnt;
for(int i=0;i<G[u].size();i++) {
int v=G[u][i];
if(!dfn[v]) {
Children++; //存一下儿子的数量
fa[v]=u; //这里用fa把图转为了树
Dfs(v);
low[u]=min(low[u],low[v]);//在回溯的过程中更新low
if(low[v]>dfn[u]) { //判断割边
edge k;
k.u=min(u,v);
k.v=max(u,v);
Bridge.insert(k);//存入
}
if(!fa[u]&&Children>=2)//是根节点的情况
Point.insert(u);
else if(fa[u]&&low[v]>=dfn[u])//不是根节点的情况
Point.insert(u);
}
else if(v!=fa[u]) { //如果通过(u,v)到达u上面的节点
low[u]=min(low[u],dfn[v]);//说明是返祖边,更新low
}
}
}
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++) {
int u,v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);//无向图存双向
}
Dfs(1);
bool first=1,flag=0;//以后为输出
if(Point.empty())
printf("Null");
while(!Point.empty()) {
int Ans=*Point.begin();
if(first) {G();
printf("%d",Ans);
first=0;
}
else printf(" %d",Ans);
Point.erase(Ans);
}
puts("");
while(!Bridge.empty()) {
edge Ans=*Bridge.begin();
printf("%d %d\n",Ans.u,Ans.v);
Bridge.erase(Ans);G();
}
}