题意:给出一个有向图G,寻找所有的sink点。“sink”的定义为:{v∈V|∀w∈V:(v→w)⇒(w→v)},对于一个点v,所有能到达的所有节点w,都能够回到v,这样的点v称为sink。
分析:由(v→w),(w→v)可知,节点v,w构成强连通,很自然的想到要缩点。缩点之后,DAG上的每一条边,都是单向的(v->w),无回路(w->v)。
错误:对于v可达的点w,不仅是直接连边——从一个强连通子集A到另一个强连通子集B,意味着,子集A中的点都不可能是sink点。
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std; const int MAXN=; struct Edge{
int v,next;
Edge(){}
Edge(int _v,int _next):v(_v),next(_next){}
}edge[MAXN*MAXN]; int head[MAXN],tol;
int pre[MAXN],low[MAXN],sccno[MAXN],dfs_clock,scc_cnt;
int vis[MAXN]; stack<int>stk; void init(int n)
{
tol=;
memset(head,-,sizeof(head));
} void add(int u,int v)
{
edge[tol]=Edge(v,head[u]);
head[u]=tol++;
} void dfs(int u)
{
int v;
pre[u]=low[u]=++dfs_clock;
stk.push(u);
for(int i=head[u];i!=-;i=edge[i].next)
{
v=edge[i].v;
if(!pre[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],pre[v]);
}
if(pre[u]==low[u]){
scc_cnt++;
do{
v=stk.top();
stk.pop();
sccno[v]=scc_cnt;
}while(u!=v);
}
} void find_scc(int n)
{
dfs_clock=scc_cnt=;
memset(pre,,sizeof(pre));
memset(low,,sizeof(low));
memset(sccno,,sizeof(sccno)); for(int i=;i<=n;i++)
if(!pre[i])
dfs(i);
} void check(int n)
{
memset(vis,,sizeof(vis));
for(int i=;i<=n;i++)
{
for(int j=head[i];j!=-;j=edge[j].next)
{
int v=edge[j].v;
if(sccno[i]!=sccno[v])
vis[sccno[i]]=;
}
}
} void print(int n)
{
int cnt=;
for(int i=;i<=n;i++)
if(!vis[sccno[i]]){
if(cnt==)printf("%d",i);
else printf(" %d",i);
cnt++;
}
printf("\n");
} int main()
{
int n,m;
while(~scanf("%d",&n))
{
if(!n)
return ;
scanf("%d",&m); init(n);
for(int i=;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
} find_scc(n); check(n); print(n);
}
return ;
}