tarjan算法求连通分量的核心还是聚类,为每个节点设置后续遍历序号dfn、节点最早追溯序号low
low(u) = min(low(v))(v 为 u 后续)
low(u) = min(dfn(v))(u 为 v 后续)
low(u)= min(low(u),dfn(u))
桥(u,v): low(v) > dfn(u)
割点 u :
u为根 & 子树大于1
u不为根 & 存在边(u,v)使dfn(u)<= low(v)
块:在该点集内的点没有桥
块与块之间通过桥连通
入度,出度:对于压缩点后的有向图来说,每个点均有入度出度,若某个点存在入度或出度为0,则该点必不可能处于其他强连通分量内
int block, idx, low[MAXN], dfn[MAXN], vis[MAXN]; void tarjan ( int u ) { int v; low[u] = dfn[u] = idx++; for ( int i = head[u]; ~i; i = edge[i].next ) { v = edge[i].to; // 无向图 if ( vist[i >> 1] ) { continue; } vist[i >> 1] = 1; if ( !dfn[v] ) { tarjan ( v ); low[u] = min ( low[u], low[v] ); block += dfn[u] < low[v]; } else if ( dfn[v] < low[u] ) { low[u] = dfn[v]; } } }
确定每个点所属块,可引入模拟堆栈
int block, top, mstk[MAXN], isin[MAXN], idx, low[MAXN], dfn[MAXN], belong[MAXN]; void tarjan ( int u ) { int v; mstk[top++] = u; isin[u] = 1; low[u] = dfn[u] = ++idx; for ( int i = head[u]; ~i; i = edge[i].next ) { v = edge[i].to; if ( !dfn[v] ) { tarjan ( v ); low[u] = min ( low[u], low[v] ); } else if ( isin[v] && dfn[v] < low[u] ) { low[u] = dfn[v]; } } if ( low[u] == dfn[u] ) { block++; do { v = mstk[--top]; isin[v] = 0; belong[v] = block; } while ( v != u ); } }
上述代码中 isin[v] 仅在有向图中有用
在连通分支内寻找环、割点
void tarjan ( int u ) { int v, k; mstk[top++] = u; low[u] = dfn[u] = ++idx; for ( int i = head[u]; ~i; i = edge[i].next ) { if ( vis[i >> 1] ) { continue; } vis[i >> 1] = 1; v = edge[i].to; if ( !dfn[v] ) { tarjan ( v ); low[u] = min ( low[u], low[v] ); if ( low[v] >= dfn[u] ) { block = 0; clr(isin); do { k = mstk[--top]; isin[k] = 1; pnum[block++] = k; } while ( k != v ); pnum[block++] = u; isin[u] = 1; getCount(); } } else if ( dfn[v] < low[u] ) { low[u] = dfn[v]; } } }
入门题:hdu 1269 http://acm.hdu.edu.cn/showproblem.php?pid=1269
基础题:hdu 2767 http://acm.hdu.edu.cn/showproblem.php?pid=2767
提高题:hdu 3394 http://acm.hdu.edu.cn/showproblem.php?pid=3394
应用题(树DP):hdu 4612 http://acm.hdu.edu.cn/showproblem.php?pid=4612
hdu 3639 http://acm.hdu.edu.cn/showproblem.php?pid=3639
连通分量合集
最新推荐文章于 2024-10-25 09:23:58 发布