Tarjan学习笔记

很久之前就想学 T a r j a n Tarjan Tarjan了,然而由于各种奇怪的原因直到上过课才学会。

于是来写一篇学习笔记 & \& &题解。。。

T a r j a n Tarjan Tarjan R o b e r t    T a r j a n Robert\;Tarjan RobertTarjan发明的一种基于 D F S DFS DFS的求解强连通分量及割点等图论问题的算法,复杂度 O ( n + m ) O(n+m) O(n+m),十分高效。

T a r j a n Tarjan Tarjan使用一个栈来维护当前搜过的路径信息,并使用 D F S DFS DFS序时间戳来判断是否找到了环。

d f n i dfn_i dfni表示点 i i i D F S DFS DFS序, l o w i low_i lowi表示从点 i i i出发能到达的点中最小的 D F S DFS DFS序,每次遇到已入栈的点就不断退栈染色即可。。。

代码实现不算麻烦:

int cnt;
int col;
void tarjan(int u)
{
    static stack<int> st;
    low[u]=dfn[u]=++cnt;
    vis[u]=1;
    st.push(u);
    for(int i=head[u];i;i=E[i].next)
    {
        int v=E[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
        {
            if(vis[v])
            {
                low[u]=min(low[u],dfn[v]);
            }
        }
    }
    if(dfn[u]==low[u])
    {
        int t=-1;
        ++col;
        while(t!=u)
        {
            t=st.top();
            st.pop();
            color[t]=col;
            vis[t]=0;
        }
    }
}

如果要把求出的强联通分量缩点:

void shrink_point()
{
    for(int i=1;i<=n;++i)
    {
        if(!dfn[i])tarjan(i);
    }
    for(int u=1;u<=n;++u)
    {
        for(int i=head[u];i;i=E[i].next)
        {
            int v=E[i].to;
            if(color[u]!=color[v])
            {
                _add(color[u],color[v]);
            }
        }
    }
}

求解 2 _ S A T 2\_SAT 2_SAT问题:

bool two_SAT()
{
    for(int i=1;i<=n<<1;++i)
    {
        if(!dfn[i])tarjan(i);
    }
    for(int i=1;i<=n;++i)
    {
        if(color[i]==color[i+n])return 0;
    }
    return 1;
}

然后变量 a i a_i ai t r u e true true对应 [ 1 , n ] [1,n] [1,n]的点编号,取 f a l s e false false对应 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]的点编号。

然后 a i a_i ai的取值就是 c o l o r [ i ] &gt; c o l o r [ i + n ] color[i]&gt;color[i+n] color[i]>color[i+n]

求解割点:

void tarjan(int u,int rt)
{
    int rec=0;
    static int cnt=0;
    static stack<int> st;
    low[u]=dfn[u]=++cnt;
    vis[u]=1;
    st.push(u);
    for(int i=head[u];i;i=E[i].next)
    {
        int v=E[i].to;
        if(!dfn[v])
        {
            tarjan(v,rt);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=rt)
            {
                cut[u]=1;
            }
            if(u==rt)
            {
                ++rec;
            }
        }
        low[u]=min(low[u],dfn[v]);
    }
    if(u==rt&&rec>1)cut[rt]=1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值