【题解】求强连通分量

题面

【题目描述】
输入一个图,输出该图中的最大强连通分量。
【输入】
第一行:n和m(n<=10000,m<=100000,n为节点个数,m为边的条数)
接下来m行,每行两个数:a,b,表示a指向b的边(a,b为非负整数);
【输出】
输出最大强连通分量的节点,按照节点编号从小到大输出,如果有多个强连通分量节点数相同,则输出节点编号字典序较小的。
【样例输入】

6 8
0 2
2 4
4 5
0 1
3 0
1 3
3 5
2 3

【样例输出】

0 1 2 3

算法分析

tarjan求强连通分量的模板题目。但是有一些细节需要注意,需要求解强连通分量中结点数最多的,且要输出这个强连通分量包含的结点,结点数目相同,输出结点编号最小的。
【解题思路】
(1)使用tarjan算法求解强连通分量;
(2)统计每个强连通分量的结点数目,且记录每个强连通分量中结点的最小编号
(3)找出强连通分量中的最大结点数Max;
(4)遍历所以强连通分量,将结点数为Max的强连通分量进行比较,比较记录的最小编号,保存结点编号最小的强连通分量的编号S;
(5)输出属于编号为S的强连通分量的结点。

参考程序

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stack>
using namespace std;
stack <int >s;
int n,m,h[10010],timee,low[10010],dfn[10010],now[10010];
int scc[10010],zh,a[10010],b[10010],maxn;
int fist[100010],to[100010],nex[100010],t;
void add(int x,int y)
{
    to[++t]=y;
    nex[t]=fist[x];
    fist[x]=t;
    return;
}
int read(){ 		//快读
    int k=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
    return k;
}
void tarjin(int u)
{
    low[u]=dfn[u]=++timee;
    s.push(u);
    now[u]=1;
    h[u]=1;
    int v;
    for(int i=fist[u];i!=-1;i=nex[i])
    {
        v=to[i];
        if(h[v]==0)
        {
            tarjin(v);
            low[u]=min(low[u],low[v]);  
        }
        else if(now[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        zh++;
        do
        {
            v=s.top();
            s.pop();
            scc[v]=zh;
            a[zh]++;
            now[v]=0;
        }while(u!=v);
        if(a[zh]>maxn) maxn=a[zh];
    }
    return ;
}
int main()
{
    n=read();m=read();
    int x,y;
    for(int i=0;i<n;i++)
        fist[i]=-1;
    for(int i=1;i<=m;i++)
    {
        x=read();y=read();
        add(x,y);
    }
    for(int i=0;i<n;i++)
        if(h[i]==0) tarjin(i);	//tarjan求强连通分量
    for(int i=1;i<=zh;i++)
    {
        if(a[i]==maxn) b[i]=1;	//统计结点数最大的强连通分量,进行标记
    }
    int da=0;
    for(int i=0;i<n;i++)
    {
        if(b[scc[i]]) {da=scc[i];break;}	//找出最小一个结点属于标记的强连通分量
    }
    for(int i=0;i<n;i++)
        if(scc[i]==da)
          printf("%d ",i);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值