NVIDIA RTX wallpaper
575 字
3 分钟
【OI考古】图论 | 割点和桥
割点
对于一个无向图 ,如果把一个点 删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点(又称割顶)。
模板题: 洛谷 P3388 - [模板]割点(割顶)
题目描述
给出一个 个点, 条边的无向图,求图的割点。
输入格式
第一行输入两个正整数 。
下面 行每行输入两个正整数 表示 到 有一条边。
输出格式
第一行输出割点个数。
第二行按照节点编号从小到大输出节点,用空格隔开。
输入输出样例
输入
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
输出
1
5
说明/提示
对于全部数据, , 。
点的编号均大于 小于等于 。
tarjan 图不一定联通。
Tarjan 算法求割点
解决这类连通性相关的图论问题通常都要请出 Tarjan 算法。关于 Tarjan 算法的基本实现思路可以参考这里。
思路
在无向图中使用 Tarjan 算法遍历图时,若对于某个点 ,若有dfn[u]=low[u]
,说明从 向下 DFS 无法回到其祖先节点,这意味着以 为根的 DFS 生成子树下的所有节点和 的祖先节点之间的路全都经过 ,若去掉 则图将不连通,说明 就是割点。
解决方案
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
const int maxn = 100500;
int n, m, cnt = -1, cc;
int head[maxn], dfn[maxn], low[maxn], fa[maxn], sum;
bool ans[maxn];
struct Node
{
int to, next;
} edge[maxn*2];
void addedge(int u, int v)
{
++cnt;
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
}
void tarjan(int u)
{
int son = 0, root;
++cc;
dfn[u] = cc;
low[u] = cc;
for(int i=head[u]; ~i; i=edge[i].next)
{
int v = edge[i].to;
if(fa[u]!=v)
{
if(!dfn[v])
{
fa[v] = u;
tarjan(v);
low[u] = min(low[u], low[v]);
if(low[v]>=dfn[u])
{
ans[u] = true;
if(fa[u]==u)
{
root = u;
son++;
}
}
}
else low[u] = min(low[u], dfn[v]);
}
}
if(son==1) ans[root] = false;
}
int main()
{
scanf("%d %d", &n, &m);
memset(head, -1, sizeof(head));
for(int i=1; i<=m; i++)
{
int u, v;
scanf("%d %d", &u, &v);
fa[u] = u;
fa[v] = v;
addedge(u, v);
addedge(v, u);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i]) tarjan(i);
}
for(int i=1; i<=n; i++)
{
if(ans[i]) sum++;
}
printf("%d\n", sum);
for(int i=1; i<=n; i++)
{
if(ans[i]) printf("%d ", i);
}
return 0;
}