割边

image

DFS 生成树

对于一张连通的无向图,我们可以从任意一点开始 DFS,得到原图的一棵 DFS 生成树(以开始 DFS 的那个点为根),这棵生成树上的边称作 树边,不在生成树上的边称作 非树边。
无向图生成树里面只有树边和非树边(返祖边)。 (这里可以对比有向图,有4种边)

割边只能是树边。为什么不能是非树边(返祖边)?
因为dfs生成树已经是连通的,删除返祖边明显对连通性没影响。

重边明显不可能是割边,割不断啊

实现
下面代码实现了对 无重边 的无向图求割边,其中,当 isbridge[v] 为真时,(father[v],v) 为一条割边。

想一想为啥记录v点就可以?

答案: 因为v的father是唯一的。 但一个father可以对应多个son

有时我们会用一个pair直接存储bridge的两边。好像更方便

int low[MAXN], dfn[MAXN], idx;
bool isbridge[MAXN];
vector<int> G[MAXN];
int cnt_bridge;
int father[MAXN];

void tarjan(int u, int fa) {
  father[u] = fa;
  low[u] = dfn[u] = ++idx;
  for (const auto &v : G[u]) {
    if (!dfn[v]) {
      tarjan(v, u);
      low[u] = min(low[u], low[v]);
      if (low[v] > dfn[u]) {
        isbridge[v] = true;
        ++cnt_bridge;
      }
    } else if (v != fa) {
      low[u] = min(low[u], dfn[v]);
    }
  }
}

上述代码在有重边的无向图上是有问题的。
参考oiwiki可以增加一个flag 判断是否已有一条边抵达父节点。第一次遇到,就跳过。后续遇到,就要算做重边,当做正常的返祖边来计算。
https://oi-wiki.org/graph/cut/#割边有重边时

int low[MAXN], dfn[MAXN], idx;
bool isbridge[MAXN];
vector<int> G[MAXN];
int cnt_bridge;
int father[MAXN];

void tarjan(int u, int fa) {
  bool flag = false;
  father[u] = fa;
  low[u] = dfn[u] = ++idx;
  for (const auto &v : G[u]) {
    if (!dfn[v]) {
      tarjan(v, u);
      low[u] = min(low[u], low[v]);
      if (low[v] > dfn[u]) {
        isbridge[v] = true;
        ++cnt_bridge;
      }
    } else {
      if (v != fa || flag)
        low[u] = min(low[u], dfn[v]);
      else
        flag = true;
    }
  }
}
posted @ 2025-05-15 14:18  katago  阅读(10)  评论(0)    收藏  举报