1.调用时先初始化 用add(x,y)增加边,调用solve()后才能缩点 //已测试三题 AC 代码确保无误
2.N是点数 M是边数 (点是从1到n)
3.bcc 记录第i个连通分量的点 belong表示点属于哪个连通分量 sent表示联通分量个数
4.g是缩点后的树
5.du记录缩点后的图的每个联通分支出度(可能会有很多重复边)
const int N=10010;
const int M=100100;
vector<int> bcc[N],g[N];
bool insta[N];
int head[N],dfn[N],low[N],sta[N],belong[N],du[N];
int nu,top,tot,sent,n,m;
struct edge
{
int from,to,nex;
}e[M];
void add(int from,int to)
{
e[nu].from=from;
e[nu].to=to;
e[nu].nex=head[from];
head[from]=nu++;
}
void tarjan(int x)
{
low[x]=dfn[x]=++tot;
sta[top++]=x;
insta[x]=true;
for(int i=head[x];i!=-1;i=e[i].nex)
{
int y=e[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(insta[y]&&low[x]>dfn[y])
low[x]=dfn[y];
}
if(low[x]==dfn[x])
{
int t;
sent++;
bcc[sent].clear();
do
{
t=sta[--top];
insta[t]=false;
belong[t]=sent;
bcc[sent].push_back(t);
}while(t!=x);
}
}
void init()
{
nu=1;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(insta,false,sizeof(insta));
memset(du,0,sizeof(du));
}
void solve()
{ sent=top=tot=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
}
void suodian()
{
int x,y;
for(int i=1;i<=sent;i++)g[i].clear();
for(int i=1;i<nu;i++)
{
x=belong[e[i].from],y=belong[e[i].to];
if(x==y)continue;
g[x].push_back(y);
du[x]++;
}
}
调用范例(POJ2553 求出度为零的连通分量)
int main()
{
int x,y;
while(scanf("%d",&n)!=EOF&&n)
{
scanf("%d",&m);
init();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
solve();
suodian();
vector<int> ans;
ans.clear();
for(int i=1;i<=sent;i++)
{
if(!du[i])
{
for(int j=0;j<bcc[i].size();j++)
ans.push_back(bcc[i][j]);
}
}
sort(ans.begin(),ans.end());
int siz=ans.size();
for(int i=0;i<siz;i++)
{
if(i)printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
return 0;
}