tarjan求割点
首先分享一篇很不错的blog
在tarjan求强连通分量时,有一个dfn数组和一个low数组,分别记录了dfs遍历树的次序,和通过不在搜索序列上的边能到达的最小的dfn的值,如果对于一个点 a 和它的子节点 b ,在遍历的时候如果子节点 b 没有办法通过一个不在搜索序列边到达更小的一个dfn的话(即不能到达比 a 的dfn小的位置)即,
low[b] >= dfn[a];
说明没有办法通过 b 不经过 a 访问到 a 之前的节点,也就说明 a 时一个割点
特别的
如果点 a 是搜索树的根节点(深度优先遍历的起点),则需要至少两个满足上述条件的值,如果多于1个点满足条件的话,显然不通过 a 点就不能访问的其他 a 的邻接点,因此 a 是割点,但是如果只有一个,就成了一个形如a -> b -> c的结构,显然把端点 a 去掉没有任何影响
例子
Input
5 5
1 2
2 3
1 3
3 4
4 5
Output
3 4
2
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define mem(a) memset(a, 0, sizeof (a))
using namespace std;
const int N = 1e5 + 10;
vector<int> g[N];
int low[N], dfn[N], st[N], cut[N], v[N], top, n, x, num, m;
void pre_work(){
for (int i = 0; i < N; i++)g[i].clear();
top = num = 0;
mem(low), mem(dfn), mem(st), mem(cut), mem(v);
}
void init(){
for (int i = 0; i < m; i++){
int a, b;
scanf("%d %d", &a, &b);
g[a].push_back(b);
g[b].push_back(a);
}
}
int tarjan(int x){
dfn[x] = low[x] = ++num;st[++top] = x;v[x] = 1;
int cnt = 0;
for (vector<int>::iterator it = g[x].begin(); it != g[x].end(); it++){
int t = *it;
if(dfn[t] == 0){
tarjan(t);
low[x] = min(low[x], low[t]);
if(dfn[x] <= low[t])cnt++;
}
else if(v[t]){
low[x] = min(low[x], dfn[t]);
}
}
if(dfn[x] == low[x]){
while (top > 0){
int Temp = st[top--];
v[Temp] = 0;
if(Temp == x)break;
}
}
if(cnt >= 1)cut[x] = 1;
//计算过程中每个点都按照不是根的情况计算
//最后返回根节点的满足dfn[x] <= low[t]的个数
//如果大于等于两个,cut[root] = 1;
//否在 cut[root] = 0;
return cnt;
}
void solve(){
pre_work();
init();
for (int i = 1; i <= n; i++){
if(!dfn[i]){
int temp = tarjan(i);
if(temp < 2)cut[i] = 0;
}
}
int res = 0;
for (int i = 1; i <= n; i++)if(cut[i])printf("%d ", i), ++res;
puts("");
printf("%d\n", res);
}
int main()
{
while (~scanf("%d %d", &n, &m) && (n + m)){
solve();
}
return 0;
}
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/12862045.html

浙公网安备 33010602011771号