点双和边双的区别我在上一篇文章中已经讨论过了,这篇文章讲边双的求法。
由于是边双,就决定了边双中一定不含有桥,但是可以含有割顶。
所以我们对边双唯一的限制条件就是不经过桥。
如此一来,我们可以分成两次dfs,第一次求出所有的桥,第二次dfs时遍历整张图,只要保证不经过桥就可以了。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <stack> using namespace std; const int maxn = 105, maxm = maxn * 2; int n, m, tot, dfs_clock, bcc_cnt; int h[maxn], dfn[maxn], low[maxn], iscut[maxn], vis[maxn]; struct edge { int v, next, isbridge; }a[maxm]; vector<int> bcc[maxn]; void add(int x, int y) { a[tot].v = y; a[tot].next = h[x]; h[x] = tot++; } int dfs(int u, int fa) { int lowu = dfn[u] = ++dfs_clock; int child = 0; for (int i = h[u]; ~i; i = a[i].next) { int v = a[i].v; if (!dfn[v]) { child++; int lowv = dfs(v, u); lowu = min(lowu, lowv); if (lowv >= dfn[u]) { iscut[u] = 1; } if (lowv > dfn[u]) { // printf("%d - %d ", u, v); a[i].isbridge = 1; a[i^1].isbridge = 1; } }else if (dfn[v] < dfn[u] && v != fa) { lowu = min(lowu, dfn[v]); } } if (fa == 0 && child == 1) { iscut[u] = 0; } low[u] = lowu; return lowu; } void dfs2(int u, int fa) { bcc[bcc_cnt].push_back(u); vis[u] = 1; for (int i = h[u]; ~i; i = a[i].next) { int v = a[i].v; if (v == fa || a[i].isbridge ||vis[v]) continue; dfs2(v, u); } } int main() { freopen("无向图双联通分量.in","r",stdin); scanf("%d%d", &n, &m); memset(h, -1, sizeof h); tot = dfs_clock = 0; for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); add(x, y); add(y, x); } dfs(1, 0); for (int i = 1; i <= n; i++) if (!vis[i]) { bcc_cnt++; bcc[bcc_cnt].clear(); dfs2(i, 0); } for (int i = 1; i <= bcc_cnt; i++) { for (int j = 0; j < bcc[i].size(); j++) printf("%d ", bcc[i][j]); printf(" "); } return 0; }