洛谷-图的遍历-P2921 [USACO08DEC]在农场万圣节Trick or Treat on the Farm

Tarjan求强连通分量问题

塔杨算法的前提是:图为有向图。

强连通;强连通图;极大连通子图(强连通分量)。

实现方法:

dfn[i]:dfs遍历第i个点的次序(时间戳)。

low[i]:还在栈中的可以到达i的最小的时间戳。(按从栈顶到栈底的顺序,i点所能到达的最早的点)

stack[ ]:存放当前遍历过但还没有确定其所属的连通分量的点。

当下一个点已经在栈中:

证明从在栈中下一个点到该点都为一个连通分量。

因为每一个连通分量只需要一个点来代表他,所以除了最前面的点dfn[i] == low[i],其他点dfn[i] != low[i]。

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100005;
int next[Maxn];
int ans[Maxn];
int head[Maxn],cnt;
struct road
{
    int to,next;
}e[Maxn*2];
void add(int a,int b)
{
    cnt++;
    e[cnt].to=b;
    e[cnt].next=head[a];
    head[a]=cnt;
}
int sum,color[Maxn],low[Maxn],ins[Maxn],tim[Maxn],sta[Maxn],top=1,col;
int Lemon[Maxn];
void Tarjan(int x)
{
    sum++;
    tim[x]=low[x]=sum;
    sta[top]=x;
    top++;
    ins[x]=1;
    for(int i=head[x];i!=0;i=e[i].next)
    {
        if(ins[e[i].to]==0)
        {
            Tarjan(e[i].to);
            low[x]=min(low[x],low[e[i].to]);
        }
        else if(ins[e[i].to]==1)
                low[x]=min(low[x],tim[e[i].to]);
    }
    if(tim[x]==low[x])
    {
        col++;
        do
        {
            top--;
            color[sta[top]]=col;
            ins[sta[top]]=-1;
        }while(sta[top]!=x);
    }
    return ;
}
void search(int root,int x,int step)
{
    if(ans[x]!=0)
    {
        ans[root]=ans[x]+1;
        return ;
    }
    else 
    {
        search(x,next[x],step+1);
        ans[root]=ans[x]+1;
    } 
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&next[i]);
        add(i,next[i]);
        if(next[i]==i) ans[i]=1;//注意特判环为1的情况。
    }
    for(int i=1;i<=n;i++)
        if(ins[i]==0) Tarjan(i);
    for(int i=1;i<=n;i++)
        Lemon[color[i]]++;//记录环的大小
    for(int i=1;i<=n;i++)
        if(Lemon[color[i]]!=1) ans[i]=Lemon[color[i]];//处理在环内的点
    for(int i=1;i<=n;i++)
        if(ans[i]==0) search(i,next[i],1);//处理在环外的点。
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/adgf/p/12332366.html