Tarjan 小结

Quetion : 判断in_stack[]有什么意义?


<0>
    if(dfn[i] != 0) low[u] = min(low[u], dfn[v]);
    当只是强连通搞缩点时,可以换为min(low[u], low[v]),因为缩点只要把相邻的一块搞跟min(low[xxx])一块儿就行了;
    但是搞双连通就不可以了,因为双连通对割点/桥的判定需要用到low[v],后面略..


    
    无向图的Tarjan判断边是否出现过仅仅为了防止逆向边的使用...我还以为有什么神奇的作用呢...-_-|| 如果是有向图的话就免了
    当重边只算一条的时侯,既可以用上诉的判边法,也可用判点法,即记录每个节点在dfs树的父节点,然后每次都判断一下fa[u] == v ?



<1> 求强连通块    //应用: 缩点


void Tarjan(int u)
{
    dfn[u] = low[u] = Index++;
    S[++top] = i;
    for(int k = head[u]; k != -1; k = edge[k].next) {
                    //TODO      什么时候要判断点v或者边k是否出现过?
        int v = edge[k].v;
        if(!dfn[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if(in_stack[v]) low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u]) {
        Order++;
        int x;
        do {
            x = S[top--];
            in_stack[x] = false;
            belong[x] = Order;
        } while(x != u);
    }
}
void solve()
{
    for(int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i);
}









 

<2> 割点u的充要条件:   (1) u为树根,且u有多于一个子树。 
                        (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。
    桥(u,v)的充要条件: 当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)


<3>[求双连通分支]


双连通分支: 图的极大双连通子图。我喜欢称之为“块”。
下面要分开讨论点双连通分支与边双连通分支的求法。


对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支。


对于边双连通分支,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。






*** 上面方法是用栈来存边 *** 其实也可以用栈来存点来搞的  ***
当每次dfn[u] <= low[v]时就是遇到一个割点,这个时候就从栈顶把元素一个一个取出,直到点v。注意,是点v,不是点u。因为点u是割点,可能存在于多个双连通分量里面,所以它要继续呆在栈里面。当然,当前双连通分量是包括u的,虽然不取出来,但还是要包括的。
所以说,其实存边和存点是一样的。 






























<4>[构造双连通图]


方法简述:缩点,对缩之后的点建图,统计其叶节点个数leave,则构造双连通图所需要的边为(leave+1)/2.




一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。


统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。


题目:poj_3352























































































































































































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值