旅游电车

【题目描述】

首都有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\n");
            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("\n");
        for (int a=1;a<=n;a++) //初始化。
          S[a].clear();
    }
    return 0;
}

/*
    反思教训:
        没看到题目中“所能到达的节点必须能够返回”的条件,还得要认真细心。
*/
posted @ 2016-09-04 09:39  前前前世。  阅读(182)  评论(0编辑  收藏  举报