题目链接: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 "); 132 else 133 printf("No "); 134 } 135 return 0; 136 }