Going from U to V or from V to U?

【题目描述】

现给定一幅有向图,选择两个点V和U,要求V能到达U或U能到达V。

询问对于图中的每一点对(V,U)是否都能满足条件,如果可以,输出“Yes”,否则,输出“No”。

【输入描述】

第一行输入一个正整数T,表示数据组数;

接下来输入T组数据,每组数据输入格式如下:

第一行输入两个整数n、m(0 < n < 1001,m < 6000),分别表示节点数和边数;

接下来m行,每行输入两个整数U、V,表示节点U和节点V之间存在一条有向边。

【输出描述】

对于每组输入数据,输出一行,包含一个“Yes”或“No”。

【输入样例】

1

3 3

1 2

2 3

3 1

【输出样例】

Yes

源代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
vector <int> List[1001],Edge[1001];
stack <int> S;
queue <int> Q;
int T,n,m,Num,Number,i[1001],j[1001],Head[1001],InSum[1001];
bool In[1001];
void Tarjan(int T) //朴素Tarjan。
{
    i[T]=j[T]=++Num;
    S.push(T);
    In[T]=true;
    for (int a=0;a<List[T].size();a++)
    {
        int t=List[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]==i[T])
    {
        int k;
        Number++; 
        do
        {
            k=S.top();
            Head[k]=Number;
            In[k]=false;
            S.pop();
        }
        while (T!=k);
    }
}
bool Topsort() //拓扑排序。
{
    int Count(0);
    while (!Q.empty())
      Q.pop();
    for (int a=1;a<=Number;a++)
      if (!InSum[a])
      {
        Count++;
        Q.push(a);
      }
    if (Count-1)
      return false; //存在一个以上入度为0的点,注意这是在求单连通分量。
    while (!Q.empty()) //涨姿势了,利用队列实现。
    {
        int T=Q.front();
        Q.pop();
        Count=0;
        for (int a=0;a<Edge[T].size();a++)
        {
            int t=Edge[T][a];
            InSum[t]--;
            if (!InSum[t])
            {
                Count++;
                Q.push(t);
            }
        }
        if (Count-1)
          return false; //同理于上。
    }
    return true;
}
void Solve()
{
    for (int a=1;a<=n;a++)
      if (!j[a])
        Tarjan(a);
    memset(InSum,0,sizeof(InSum)); //InSum[]存储缩点后的入度数。
    for (int a=1;a<=Number;a++) //这么多初始化真是,想婊人。
      Edge[a].clear();
    for (int a=1;a<=n;a++)
      for (int b=0;b<List[a].size();b++) //缩点重新建图。
      {
        int t1=Head[a];
        int t2=Head[List[a][b]];
        if (t1!=t2)
        {
            InSum[t2]++;
            Edge[t1].push_back(t2);
        }
      }
    if (Topsort())
      printf("Yes
");
    else
      printf("No
");
}
int main() //Tarjan缩点+拓扑排序。
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        Num=Number=0; //Num表示Tarjan序编号,Number表示缩点后的点数。
        while (!S.empty()) //Tarjan栈。
          S.pop();
        for (int a=1;a<=n;a++)
        {
            List[a].clear(); //初始图。
            j[a]=In[a]=0; //j[]存储时间戳,In[]表示是否入Tarjan栈。
        }
        while (m--)
        {
            int t1,t2;
            scanf("%d%d",&t1,&t2);
            List[t1].push_back(t2);
        }
        Solve();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Ackermann/p/5930510.html