#1183 : 连通性一·割边与割点
(模板:求割边和割点)
题意:给出一个连通图,求出所有的割点和割边,没有割点的话直接输出Null。
思路:
在dfs树中:
割点:1.该点为dfs树的根结点,若这个根节点有两颗及以上的子树,则为割点
2.该点不是dfs树的根结点,若v为u的子节点,low[v]>=dfn[u]的话,表示u的子节点v最多能回到祖先节点u,不能回到u的祖先节点了,此时这也是一个节点。
割边:对于一条边<u,v>,若v为u的子节点,low[v]>dfn[u]的话,表示u的子节点v无法回到u结点,此时是<u,v>是一条割边。
以下定义转载于hihoCoder
割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。
割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。
DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。
树边:在搜索树中的蓝色线所示,可理解为在DFS过程中访问未访问节点时所经过的边,也称为父子边
回边:在搜索树中的橙色线所示,可理解为在DFS过程中遇到已访问节点时所经过的边,也称为返祖边、后向边
#include<bits/stdc++.h> using namespace std; const int maxn = 200000 + 10; struct Edge { int u,v; }; bool cmd(Edge &a,Edge &b) { if(a.u==b.u) return a.v<b.v; else return a.u<b.u; } struct tarjan { //tarjan模板 int n; vector<int>e[maxn]; //边集 int dfn[maxn]; //dfn数组是个时间戳,就是访问那个节点的时间,也就是第几次调用dfs这个函数 int low[maxn]; //Low是u的子节点能通过反向边到达的节点DFN的最小值, 初始值为dfn[u] int index; //记录dfs调用的次数 Edge edge[maxn]; //记录割边 int node[maxn]; //记录割点 int f[maxn]; //父亲节点 int s[maxn]; //儿子节点的个数 int cnt_edge; //割边的边数 int cnt_node; //割点的点数 void init(int N) { //初始化 n = N; for (int i = 1; i <= n; i++) e[i].clear(); index = 0; cnt_node=0; cnt_edge=0; memset(dfn, -1, sizeof(dfn)); memset(f,0,sizeof(f)); memset(s,0,sizeof(s)); } void addedge(int u, int v) { //添加边 e[u].push_back(v); } void dfs(int u) { //dfs类似环缩点的写法,两个主要数组dfn[]和low[] dfn[u] = low[u] = ++index; bool flag=0; for (int i = 0; i<e[u].size(); i++) { int &v = e[u][i]; if (dfn[v] == -1) { s[u]++; f[v]=u; dfs(v); if(low[v]>=dfn[u]) { flag=1; } if(low[v]>dfn[u]) { //low[v]大于dfn[u]表示除原路径外,没有其他路径使得v回到u,即无法形成环,所以是割边 edge[cnt_edge].u=min(u,v); edge[cnt_edge++].v=max(u,v); } low[u] = min(low[u], low[v]); } else if (v!=f[u]) low[u] = min(low[u], dfn[v]); } if((f[u]==0&&s[u]>=2)||(f[u]&&flag)) // 1.该点为dfs树的根结点,若这个根节点有两颗及以上的子树,则为割点 node[cnt_node++]=u; //2.该点不是dfs树的根结点,若存在v为u的子节点且low[v]>=dfn[u]的话,则也是一个割点 } void solve() { //遍历所有点 for (int i = 1; i <= n; i++) { if (dfn[i] == -1) dfs(i); } } } T; int main() { int n,m; int u,v; scanf("%d %d",&n,&m); T.init(n); for(int i=1; i<=m; i++) { scanf("%d %d",&u,&v); T.addedge(u,v); T.addedge(v,u); } T.solve(); sort(T.edge,T.edge+T.cnt_edge,cmd); sort(T.node,T.node+T.cnt_node); if(T.cnt_node==0) { printf("Null\n"); return 0; } for(int i=0; i<T.cnt_node; i++) { printf("%d%c",T.node[i],i==T.cnt_node-1?'\n':' '); } for(int i=0; i<T.cnt_edge; i++) { printf("%d %d\n",min(T.edge[i].u,T.edge[i].v),max(T.edge[i].u,T.edge[i].v)); } return 0; }
不得不说原本的hiho上的解释写的很好 ,推荐大家去看看 官方题解(应该吧)