【题目描述】
首都有N个旅游景区,电车只沿道路规定的方向行驶,为了不使投入使用的电车有可能无法回到它的起始站,人们希望知道可以在哪些景区设置站点。
一个景区可以被设置成车站,当且对于任意一个从该景区出发所能到达的景区,均至少有一条路可回到该景区。现已完成了一份景区之间的道路连通情况的报告,报告中将给出首都的景区数目N、道路总数M以及一些形如“景区A和景区B之间有一条从A到B的单向道路”的信息。根据报告中的信息,列出所有可以被设置成车站的景区。
【输入描述】
输入由多份报告组成(这些报告相互无任何联系),每份报告包括:
第一行包含两个整数N、M;
接下来M行,每行两个整数Ai、Bi表示Ai和Bi之间有一条单向道路Ai --> Bi。
当N=0时,输入结束。
对于任意景区,分别以该景区为起点或终点的道路总数均不超过50。
【输出描述】
对于每份报告,输出一行,包括所有能被设置成电车站点的景区编号,各编号之间用一个空格隔开。
【样例输入】
5 6
1 2
2 3
3 4
4 1
2 5
5 2
1 0
0
【样例输出】
1 2 3 4 5
1
【数据范围及提示】
对于40%的数据,N <= 200;
对于100%的数据,N <= 5000,M <= 50000。
源代码: #include<cstdio> #include<cstring> #include<vector> #include<stack> using namespace std; stack <int> H; vector <int> S[5001]; int n,m,Num,Ans,Head[5001],i[5001],j[5001]; bool In[5001]; void Tarjan(int t) //朴素Tarjan。 { i[t]=j[t]=++Num; In[t]=true; H.push(t); for (int a=0;a<S[t].size();a++) { int T=S[t][a]; if (!j[T]) { Tarjan(T); i[t]=min(i[t],i[T]); } else if (In[T]) i[t]=min(i[t],j[T]); } if (i[t]==j[t]) { int k; Ans++; do { k=H.top(); H.pop(); Head[k]=Ans; In[k]=false; } while (k!=t); } } int main() { while (scanf("%d",&n)==1) //读入确实有点恶心。 { if (!n) break; scanf("%d",&m); if (!m&&n==1) { printf("1 "); continue; } Num=Ans=0; memset(j,0,sizeof(j)); //似乎只需要初始化j[]就好了。 for (int a=0;a<m;a++) { int t1,t2; scanf("%d%d",&t1,&t2); S[t1].push_back(t2); } for (int a=1;a<=n;a++) if (!j[a]) Tarjan(a); int Sum1[5001]={0},Sum2[5001]={0}; //Sum1[]表示入度,Sum2[]表示出度。 for (int a=1;a<=n;a++) //缩点处理入度出度。 for (int b=0;b<S[a].size();b++) { int k=S[a][b]; if (Head[a]!=Head[k]) { Sum1[Head[k]]++; Sum2[Head[a]]++; } } for (int a=1;a<=n;a++) if (!Sum2[Head[a]]) printf("%d ",a); printf(" "); for (int a=1;a<=n;a++) //初始化。 S[a].clear(); } return 0; } /* 反思教训: 没看到题目中“所能到达的节点必须能够返回”的条件,还得要认真细心。 */