#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//链式前向星、割点(dfs)、堆
#define N 20005
#define MIN(a, b) a < b ? a : b
struct line
{
int nex;
int end;
} edge[200005];//无向图双倍
int fat[N] = { 0 };
int dn = 0;//当前时间戳
int dfn[N] = { 0 };//时间戳
int low[N] = { 0 };//不经过父节点能到最小时间戳的节点
bool flag[N] = { 0 };//是否为割点
int ans = 0;//割点数量
int read()
{
int num = 0;
char ch = getchar();
while (ch < 48 || 57 < ch) ch = getchar();
while (48 <= ch && ch <= 57)
{
num = num * 10 + ch - 48;
ch = getchar();
}
return num;
}
void add_edge(const int* hea, const int* end)
{
static int i = 0;//编号
edge[++i].end = *end;
edge[i].nex = fat[*hea];
fat[*hea] = i;//更新编号
}
void Tarjan(int id, int* root)//当前节点、该树根节点
{
dfn[id] = low[id] = ++dn;//初始化
int son_tree = 0;//子树个数
//遍历所有直接相连的边
for (int i = fat[id]; i; i = edge[i].nex)
{
int end = edge[i].end;
if (dfn[end] == 0)//还未搜索
{
son_tree++;//记录
Tarjan(end, root);//搜索
low[id] = MIN(low[id], low[end]);//更新不经过父节点能到最小时间戳的节点
if (low[end] >= dfn[id] && flag[id] == 0 && id != *root)//非根节点的点是否为割点
{
ans++, flag[id] = 1;//记录
}
}
else
{
low[id] = MIN(low[id], dfn[end]);
}
}
if (id == *root && flag[id] == 0 && son_tree >= 2)
{
ans++, flag[id] = 1;//特判根节点
}
}
void write(int num)
{
if (num > 9) write(num / 10);
putchar(num % 10 + 48);
}
int main()
{
int n = read(), m = read();//点、边数
for (int i = 1; i <= m; i++)//连接情况
{
int x = read(), y = read();
add_edge(&x, &y), add_edge(&y, &x);//加边
}
for (int i = 1; i <= m; i++)//Tarjan
{
if (dfn[i] == 0)//是否已经搜索过
{
Tarjan(i, &i);
}
}
write(ans);//快写 - 割点个数
putchar(10);
for (int i = 1; i <= n; i++)//编号从小到大输出割点
{
if (flag[i])
{
write(i), putchar(32);
}
}
return 0;
}
其实我不太理解的说,求讲解!