//求这样的sink点:它能达到的点,那个点必能达到他,即(G)={v∈V|任意w∈V:(v→w)推出(w→v)} //我法:tarjan缩点后,遍历点,如果该点到达的点不在同一个强连通中,该点排除,而且该点所在的 //的强连通分支所有点都排除(开始因为这个跪WA!慎思!) #include<iostream> //143MS, #include<vector> #include<cstdio> #include<cstring> #include<stack> #include<queue> using namespace std; int n;int m; const int MAX=5001; vector<vector<int> >edges(MAX); int visited[MAX]; int low[MAX]; int dfn[MAX]; int is_sink[MAX]; //统计出入度 int Strongly_connected_branch[MAX]; //并为一个强连通,标记为1.2.3... int num;int times; bool is_sink_all[MAX]; stack<int>s; bool instack[MAX]; void tarjan(int u) { low[u]=dfn[u]=times++; instack[u]=1; s.push(u); int len=edges[u].size(); for(int i=0;i<len;i++) { int v=edges[u][i]; if(visited[v]==0) //小心细节! { visited[v]=1; tarjan(v); if(low[u]>low[v])low[u]=low[v]; } else if(instack[v]&&low[u]>dfn[v]) //有向图,要问是否在栈中,后向边,V为U某个祖先 { low[u]=dfn[v]; } } if(dfn[u]==low[u]) //在一个SCC { num++;int temp; do { temp=s.top(); instack[temp]=0; s.pop(); Strongly_connected_branch[temp]=num; } while(temp!=u); } } void initialize() { num=times=0; for(int i=0;i<=n;i++) { instack[i]=low[i]=dfn[i]=visited[i]=0; edges[i].clear(); is_sink_all[i]=is_sink[i]=1; Strongly_connected_branch[i]=-1; } } bool readin() { scanf("%d",&n); if(n==0)return 0; scanf("%d",&m); initialize(); int from,to; for(int i=1;i<=m;i++) { scanf("%d%d",&from,&to); edges[from].push_back(to); } return 1; } void solve() { for(int i=1;i<=n;i++) if(visited[i]==0) { visited[i]=1; tarjan(i); } for(int i=1;i<=n;i++) //自己思得:枚举所有边,缩点只是把所有SCC分开 { int len=edges[i].size(); for(int j=0;j<len;j++) { int v=edges[i][j]; if(Strongly_connected_branch[v]!=Strongly_connected_branch[i])//b不再用一个强连通分支 { is_sink[i]=0; is_sink_all[Strongly_connected_branch[i]]=0; //其所在强连通全跪! break; } } } queue<int>q; //要按顺序输出,无奈。 for(int i=1;i<=n;i++) { if(is_sink_all[Strongly_connected_branch[i]]==0){continue;} if(is_sink[i]==1)q.push(i); } while(!q.empty()) { int cur=q.front(); if(q.size()==1)printf("%d ",cur); else printf("%d ",cur); q.pop(); } } int main() //代码越来越清楚O(∩_∩)O~ { while(readin()) { solve(); } return 0; }