最后复习之强连通分量

这里写图片描述
不知道noip考不考这个,但是既然复习到了为了退役之后不忘还是写一下吧。关于这个强连通分量我只知道一个tarjan算法。
基本过程:dfs一个图把搜到的点进栈,对每个点进行标两个序号,一个表示深搜的顺序,另一个表示这个点所能直接或间接连到的被搜索到的最早的点,要一直维护第二个值,在回溯的时候(子树处理完后)如果这个点的两序号相等,则这个点就是这个强连通分量最早被搜到的点,那么这个时候进行出栈操作,从栈顶到这个点都是当前搜到的强连通分量里的点(一个点也可以是强连通分量,会出栈,使得之后找到的强连通分量里不包含这个点,当然如果这个点属于别的强连通分量里就不会单独出栈了)。
不是很纯粹的模板题codevs 2822 爱在心中
除了找所有的强连通分量之外还要对这些强连通分量进行判断,如果这个强连通分量里所有的点的所有边都在强连通分量内部,那么这个就是所求的强连通分量,如果出现了多个那么所求的强连通分量就不存在。因为如果这个强连通分量有向外的边,那么这个强连通分量就不可能是最终答案,可以联系一下并查集找最终的中枢。

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
vector<int> bian[100010];
int dfn[50010],low[50010],cnt=0; //dfn是dfs的顺序,low是能走到的顺序最早的点,cnt用于标序号
int belong[50010],num=0,ans=0; //属于哪一个强连通分量,有多少个强连通分量,目标强连通分量
int zhan[50010],tot=0; //栈,栈顶
bool inzhan[50010]={0};
int n,m;
bool pd=0;
int min(int a,int b)
{
    return (a<b)?a:b;
}
void tarjan(int d)
{
    dfn[d]=low[d]=++cnt;
    zhan[++tot]=d;
    inzhan[d]=1;
    int len=bian[d].size(),a;
    for (int i=0;i<len;i++)
    {
        a=bian[d][i];
        if (!dfn[a])
        {
            tarjan(a);
            low[d]=min(low[d],low[a]);
        }
        else if (inzhan[a]) low[d]=min(low[d],dfn[a]);
    }
    if (dfn[d]==low[d])
    {
        num++;
        a=0;
        int la=0;
        while (a!=d)
        {
            a=zhan[tot--];
            la++;
            belong[a]=num;
            inzhan[a]=0;
        }
        if (la==1) //删去大小为一的强连通分量,题目要求不能自恋。。。
        {
            belong[d]=0;
            num--;
            return ;
        }
        for (int i=tot+1;i<=tot+la;i++)
        {
            len=bian[zhan[i]].size();
            for (int j=0;j<len;j++)
                if (belong[bian[zhan[i]][j]]!=num)
                    return ;
        }
        if (ans) {pd=1;return ;}
        ans=num;
    }
}
int main()
{
    cin>>n>>m;
    int a,b;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        bian[a].push_back(b);
    }
    for (int i=1;i<=n;i++)
        if (!dfn[i])
            tarjan(i);
    cout<<num<<endl;
    if (pd||!ans) cout<<"-1";
    else
    {
        for (int i=1;i<=n;i++)
            if (belong[i]==ans)
                printf("%d ",i);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值