poj 2762(强连通分量+拓扑排序)

题目链接:http://poj.org/problem?id=2762

题意:给出一个有向图,判断任意的两个顶点(u,v)能否从u到达v,或v到达u,即单连通,输出Yes或No.

分析:对于同一个强连通分量而言,所有的点都是互达的,如果该有向图只有一个强连通分量,则肯定是Yes了;

若有多个强连通分量呢?判断两个不同的强连通分量的点u和v是否单连通,缩点后,建新图,用拓扑排序判断,删除点的时候若发现有大于2个点的入度为0,则u和v必定不能连通。

AC代码:

  1 #include<cstdio>

  2 #include<cstring>

  3 const int N=1000+5;

  4 const int M=6000+5;

  5 struct EDGE{

  6     int v,next;

  7 }edge[M],edge2[M];

  8 int first[N],low[N],dfn[N],sta[M],belong[N],que[M],in[N],first2[N];

  9 bool instack[N],map[N][N];

 10 int cnt,g,scc,top,k;

 11 void AddEdge(int u,int v)

 12 {

 13     edge[g].v=v;

 14     edge[g].next=first[u];

 15     first[u]=g++;

 16 }

 17 void AddEdge2(int u,int v)

 18 {

 19     edge2[k].v=v;

 20     edge2[k].next=first2[u];

 21     first2[u]=k++;

 22 }

 23 int min(int a,int b)

 24 {

 25     return a<b?a:b;

 26 }

 27 void Tarjan(int u)    //求强连通分量

 28 {

 29     int i,v;

 30     low[u]=dfn[u]=++cnt;

 31     sta[++top]=u;

 32     instack[u]=true;

 33     for(i=first[u];i!=-1;i=edge[i].next)

 34     {

 35         v=edge[i].v;

 36         if(!dfn[v])

 37         {

 38             Tarjan(v);

 39             low[u]=min(low[u],low[v]);

 40         }

 41         else if(instack[v])

 42             low[u]=min(low[u],dfn[v]);

 43     }

 44     if(low[u]==dfn[u])

 45     {

 46         scc++;

 47         while(1)

 48         {

 49             v=sta[top--];

 50             instack[v]=false;

 51             belong[v]=scc;    //缩点

 52             if(v==u)

 53                 break;

 54         }

 55     }

 56 }

 57 void build(int n)    //建缩点后的新图

 58 {

 59     int u,i,v,a,b;

 60     memset(map,false,sizeof(map));

 61     memset(first2,-1,sizeof(first2));

 62     memset(in,0,sizeof(in));

 63     k=0;

 64     for(u=1;u<=n;u++)      //遍历每个顶点的出边

 65     {

 66         for(i=first[u];i!=-1;i=edge[i].next)

 67         {

 68             v=edge[i].v;

 69             a=belong[u];

 70             b=belong[v];

 71             if(a==b)     //若属于同一个强连通分量

 72                 continue;

 73             if(!map[a][b])

 74             {

 75                 AddEdge2(a,b);   //建新边

 76                 map[a][b]=true;

 77                 in[b]++;

 78             }

 79         }

 80     }

 81 }

 82 int topo()   //拓扑排序

 83 {

 84     int i,front,rear,top,v;

 85     front=rear=0;

 86     for(i=1;i<=scc;i++)

 87         if(in[i]==0)

 88         {

 89             que[rear++]=i;

 90         }

 91     if(rear-front>1)    //入度为0的顶点个数大于1,则无解

 92         return 0;

 93     while(front<rear)

 94     {

 95         top=que[front++];

 96         for(i=first2[top];i!=-1;i=edge2[i].next)

 97         {

 98             v=edge2[i].v;

 99             in[v]--;

100             if(in[v]==0)

101             {

102                 que[rear++]=v;

103             }

104             if(rear-front>1)

105                 return 0;

106         }

107     }

108     return 1;      //有解

109 }

110 int main()

111 {

112     int t,n,m,i,u,v;

113     scanf("%d",&t);

114     while(t--)

115     {

116         scanf("%d%d",&n,&m);

117         g=cnt=top=scc=0;

118         memset(first,-1,sizeof(first));

119         memset(dfn,0,sizeof(dfn));

120         memset(instack,false,sizeof(instack));

121         while(m--)

122         {

123             scanf("%d%d",&u,&v);

124             AddEdge(u,v);

125         }

126         for(i=1;i<=n;i++)    //求强连通分量

127             if(!dfn[i])

128                 Tarjan(i);

129         build(n);     //建缩点后的新图

130         if(topo())    //拓扑排序

131             printf("Yes\n");

132         else

133             printf("No\n");

134     }

135     return 0;

136 }
View Code

 

你可能感兴趣的:(poj)