标签:find mem 计算 重复 pre oid ack str cut
割顶:对于无向图G,如果删除某个点u后,连通分量的数目增加, 称u为图的割顶。对于连通图,割顶就是删除之后使图不再连通的点。
割顶的求解依如下定理:
在无向连通图G的DFS树中,非根结点u是G的割顶当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(连回u)不算。
算法实现:
采用时间戳,在dfs遍历的过程中给每个节点u均标记以前序时间戳pre[u],设low[u]为u及其后代所能连回的最早的祖先的pre值,则定理中的条件就可以简写成结点u存在一个子结点v,使得 low[v] >= pre[u] 。
作为一种特殊情况,如果v的后代只能连回v自己,即 low[v] > pre[u] ,只需要删除边(u, v) 就可以让G非连通了,满足这个条件的边称为桥。换句话说,我们不仅知道结点u是割顶,还知道了(u, v)是桥。
双连通分量:
对于一个连通图,如果任意两点至少存在两条点不重复的路径,则说这个图是点-双连通的。即任意两条边都在同一个简单环内。
如果任意两点至少存在两条边不重复的路径,则说这个图是边-双连通的。即每条边都至少在一个简单环中。
对于一张无向图,点-双连通的极大子图称为双连通分量(BCC)。
性质:
1.每条边恰好属于一个双连通分量。
2.不同双连通分量最多只有一个公共点,且它一定是割顶。
3.任意割顶都是至少两个不同双连通分量的公共点。
4.桥不属于任何边-双连通分量,除了桥的每条边恰好属于一个边-双连通分量。
计算点-双连通分量的算法:
1 int n; 2 int pre[maxn], bccno[maxn], iscut[maxn], dfs_clock, bcc_cnt; 3 vector<int> G[maxn], bcc[maxn]; 4 5 struct Edge{ 6 int u, v; 7 Edge(int uu, int vv): u(uu), v(vv) {} 8 }; 9 10 stack<Edge> S; 11 12 int dfs(int u, int fa) { 13 int lowu = pre[u] = ++dfs_clock; //时间戳 14 int child = 0; //子结点数目 15 for(int i = 0; i < G[u].size(); i++) { 16 int v = G[u][i]; 17 Edge e = Edge(u, v); 18 if(!pre[v]) { //没有访问过v 19 child++; 20 S.push(e); 21 int lowv = dfs(v, u); 22 lowu = min(lowu, lowv); 23 if(lowv >= pre[u]) { 24 iscut[u] = 1; 25 bcc[++bcc_cnt].clear(); 26 for(;;) { 27 Edge x = S.top(); S.pop(); 28 if(bccno[x.u] != bcc_cnt) {bcc[bcc_cnt].push_back(x.u); bccno[x.u] = bcc_cnt;} 29 if(bccno[x.v] != bcc_cnt) {bcc[bcc_cnt].push_back(x.v); bccno[x.v] = bcc_cnt;} 30 if(x.u == u && x.v == v) break; 31 } 32 } 33 } 34 else if(pre[v] < pre[u] && v != fa) { 35 child++; 36 S.push(e); 37 lowu = min(lowu, pre[v]); 38 } 39 } 40 if(fa < 0 && child == 1) iscut[u] = 0; 41 return lowu; 42 } 43 44 void find_bcc() { 45 memset(pre, 0, sizeof pre); 46 memset(bccno, 0, sizeof bccno); 47 memset(iscut, 0, sizeof iscut); 48 dfs_clock = bcc_cnt = 0; 49 for(int u = 0; u < n; u++) if(pre[u] == 0) dfs(u, -1); 50 }
标签:find mem 计算 重复 pre oid ack str cut
原文地址:http://www.cnblogs.com/Kiraa/p/6415833.html