无向图的割点与桥
定义
割点:给定一无向连通图,对于其中一点 ,若从图中删掉 和所有与 相连的边后,原图分裂成成 个或以上不相连的子图,则称 为原图的割点(或割顶)。
割边:给定一无向连通图,对于其中一边 ,若从图中删掉 后,原图分裂成 个或以上不相连的子图,则称 为原图的割边(或桥)。
算法可以在 内求出所有割点与割边。
割点
对于无向图的每一个联通块,我们可以把他看成一棵树,即把我们假定的根节点拎起来。
先思考,怎样才能判断出从图中删掉 和所有与 相连的边后,原图会不联通。
根据之前求 强连通分量 时对 和 的定义。
:深度优先搜索遍历时结点 被搜索的次序(也称为 序)。
:定义为 所在子树的节点经过最多一条非树边 (其中 必须可达 )能到达的节点中最小的 序。
易得,当 时,说明从 和 的子树 出发,若不经过 ,则无法到达比 的 更小的节点,那么我们把 删掉,原图就被分成了 和 的子树 和 剩下的节点 至少 个子图。
若 是割点,则 满足一下任一条件:
-
不是搜索树的根节点且 至少有 个子节点满足:;
-
是搜索树根节点且 至少有 个子节点满足:。
如下图:
当 为搜索树的根节点时, 没有父亲结点,所以 至少有 个子节点满足: 时, 才是割点。
这时可能有人要问了,如果图是这样的呢? 还是割点吗?根据上面的判法不会错吗?
对于这种图,我可以明确的告诉你们,无向图是没有横向边的,把他想成这样就行了。
CODE
#include <bits/stdc++.h>
using namespace std;
struct Fastio
{
template <typename T>
inline Fastio operator>>(T &x)
{
x = 0;
char c = getchar();
while (c < '0' || c > '9')
c = getchar();
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return *this;
}
inline Fastio &operator<<(const char *str)
{
int cur = 0;
while (str[cur])putchar(str[cur++]);
return *this;
}
template <typename T>
inline Fastio &operator<<(T x)
{
if (x == 0)
{
putchar('0');
return *this;
}
if (x < 0) putchar('-'), x = -x;
static int sta[45];
int top = 0;
while (x) sta[++top] = x % 10, x /= 10;
while (top) putchar(sta[top] + '0'), --top;
return *this;
}
} io;
int n, m, rt, ans, cnt_node, cntn;
int cnt;
array<int, 2000005> head;
struct abc
{
int to, nxt;
};
array<abc, 2000005> dd;
array<bool, 2000005> cut;
array<int, 2000005> dfn, low;
inline void add(int u, int v)
{
dd[++cnt].to = v;
dd[cnt].nxt = head[u];
head[u] = cnt;
}
inline void tarjan(int u)
{
dfn[u] = low[u] = ++cnt_node;
int flag = 0;
for (int e = head[u]; e; e = dd[e].nxt)
{
int v = dd[e].to;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[v], low[u]);
if(dfn[u] <= low[v])
{
flag++;
if((u != rt || flag > 1) && !cut[u])
{
cut[u] = 1;
cntn++;
}
}
}
else low[u] = min(low[u], dfn[v]);
}
}
signed main()
{
io >> n >> m;
for(int i = 1; i <= m; ++i)
{
int u, v;
io >> u >> v;
add(u, v);
add(v, u);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(rt = i);
cout << cntn << endl;
for(int i = 1; i <= n; ++i)
if(cut[i]) io << i << " ";
return 0;
}
割边
与割点类似,易得,当 时,说明若 和 的子树不存在一条连接 或其祖先的后向边,那么我们把 删掉,原图就被分成了 和 的子树 和 剩下的节点 至少 个子图。
定理:) 为桥当且仅当它不在任何一个简单回路中。
若 是割边,则 满足条件:。
另外,对于重边,只有一条算。
所以判断两条边是否是同一条无向边,将边的编号通过 一直传递下去。
由于一条无向边是由两条有向边组成的且这两条有向边的编号是相差 ,所以我们通过一条边 的编号,得知另一条边 的编号。
判断方法:
-
若 ,则 。
-
若 ,则 。
CODE
#include <bits/stdc++.h>
using namespace std;
struct Fastio
{
template <typename T>
inline Fastio operator>>(T &x)
{
x = 0;
char c = getchar();
while (c < '0' || c > '9')
c = getchar();
while (c >= '0' && c <= '9')
x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return *this;
}
inline Fastio &operator<<(const char *str)
{
int cur = 0;
while (str[cur])putchar(str[cur++]);
return *this;
}
template <typename T>
inline Fastio &operator<<(T x)
{
if (x == 0)
{
putchar('0');
return *this;
}
if (x < 0) putchar('-'), x = -x;
static int sta[45];
int top = 0;
while (x) sta[++top] = x % 10, x /= 10;
while (top) putchar(sta[top] + '0'), --top;
return *this;
}
} io;
int n, m, cnt_node, cntn;
struct abc
{
int a, b;
} ans[2000005];
bool cmp(abc a, abc b)
{
if(a.a != b.a) return a.a < b.a;
return a.b < b.b;
}
int cnt;
array<int, 2000005> head;
struct abcd
{
int to, nxt;
};
array<abcd, 2000005> dd;
array<int, 2000005> dfn, low;
inline int js(int x)
{
return x % 2 ? x + 1 : x - 1;
}
inline void add(int u, int v)
{
dd[++cnt].to = v;
dd[cnt].nxt = head[u];
head[u] = cnt;
}
inline void tarjan(int u, int edge)
{
dfn[u] = low[u] = ++cnt_node;
for (int e = head[u]; e; e = dd[e].nxt)
{
int v = dd[e].to;
if (!dfn[v])
{
tarjan(v, e);
low[u] = min(low[v], low[u]);
if(low[v] > dfn[u]) ans[++cntn] = (abc){min(u, v), max(v, u)};
}
else if (e != js(edge))
{
low[u] = min(low[u], dfn[v]);
}
}
}
signed main()
{
io >> n >> m;
for(int i = 1; i <= m; ++i)
{
int u, v;
io >> u >> v;
add(u, v);
add(v, u);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i, 0);
sort(ans + 1, ans + cntn + 1, cmp);
for(int i = 1; i <= cntn; ++i) io << ans[i].a << " " << ans[i].b << "\n";
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122148
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话