题面
【题目描述】
输入一个图,输出该图中的最大强连通分量。
【输入】
第一行: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;
}