专栏文章
tarjan 割点 割边
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miooisqj
- 此快照首次捕获于
- 2025/12/02 22:34 3 个月前
- 此快照最后确认于
- 2025/12/02 22:34 3 个月前
割点的定义
对于一个无向图,删除一个点,图的极大联通分量增加,这个点就是一个割点。
实现步骤
很容易想到的是,暴力枚举每一条边和点,但时间复杂度太高,考虑优化。
我们可以使用 Tarjan,记录 为第 个点的时间戳, 表示通过回边(非父子边)能回到的最小时间戳的节点。
对于根节点,若其有两棵及以上的子树,则根节点为割点。
因为如果去掉根节点,两棵子树将无法互相连通。
对于不是根节点的 ,若有一儿子节点 ,无法通过回边回到除 外的其他父亲节点,则 为割点,即 。
P3388 【模板】割点(割顶)
CPP#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int n, m, dfn[N], low[N], tim, root, cnt;
bool vis[N];
vector<int> nbr[N];
void tarjan(int u) {
int sum = 0;
dfn[u] = low[u] = ++tim;
for (int v : nbr[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
sum++;
if (u != root || sum >= 2)
(vis[u] == 0) && (cnt++, vis[u] = 1);
}
} else {
low[u] = min(low[u], dfn[v]);
}
}
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
nbr[u].push_back(v);
nbr[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i])
root = i, tarjan(i);
}
cout << cnt << '\n';
for (int i = 1; i <= n; i++) {
if (vis[i])
cout << i << ' ';
}
return 0;
}
HDU-4738 Caocao's Bridges
问题转化后即求边权最小值。
注意数组范围,本题很迷。
C#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 10;
int n, m, head[N], dfn[N], low[N], tim, tot, ans;
bool bridge[2 * N * N];
struct Edge {
int v, nxt, w;
} edges[N * N * 2];
void add_edge(int u, int v, int w) {
edges[++tot].v = v;
edges[tot].nxt = head[u];
edges[tot].w = w;
head[u] = tot;
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++tim;
for (int i = head[u]; i; i = edges[i].nxt) {
int v = edges[i].v;
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) {
bridge[i] = bridge[i ^ 1] = 1;
}
} else if (i != (fa ^ 1)) {
low[u] = min(low[u], dfn[v]);
}
}
}
signed main() {
while (cin >> n >> m && n) {
memset (dfn, 0, sizeof dfn);
memset (head, 0, sizeof head);
memset (bridge, 0, sizeof bridge);
tot = 1, tim = 0;
int pre = 1e9;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
add_edge(u, v, w), add_edge(v, u, w);
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dfn[i])
cnt++, tarjan(i, 0);
}
if (cnt >= 2) {
cout << "0\n";
} else {
for (int j = 2; j <= tot; j += 2) {
if (bridge[j])
pre = min(pre, edges[j].w);
}
if (pre == 1e9)
cout << "-1\n";
else if (pre == 0)
cout << 1 << '\n';
else
cout << pre << '\n';
}
}
return 0;
}
割边(桥)定义
对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。
实现步骤
跟割点没差什么,不用考虑根节点问题,只要改成 即可。
U132350 【模板】割边(桥)
C#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, dfn[N], low[N], tim, root, cnt;
bool vis[N];
vector<int> nbr[N];
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++tim;
for (int v : nbr[u]) {
if (v == fa)
continue;
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) {
cnt++;
}
} else {
low[u] = min(low[u], dfn[v]);
}
}
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
nbr[u].push_back(v);
nbr[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i])
tarjan(i, 0);
}
cout << cnt;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...