题意: 给出一个有n 个节点的和m 条单向边,问图中是否任意两点都是连通的,这里连通的定义为:u,v连通,则存在一条从u到v的边或存在一条从v到u的边。
分析:因为在一个强连通分量内部的点是两两可达的,所以可以先对所有的强连通分量缩点,即把强连通分量内部的点看成是一个点。如果两点不可达那么在拓
扑排序时,该两点谁也不是谁的前驱和后继,所以在拓扑排序时只要同时出现至少两个入度为0的点,那么这些点一定互不可达,所以只要判断拓扑的方式
是否唯一即可。
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) #define min(a,b)(a)<(b)?(a):(b) #define maxn 1005 struct node { int to,next; }e[100000],edge[1000000]; int tot; int tt; int head[maxn]; int toph[maxn]; void add(int s,int t) { e[tot].to=t; e[tot].next=head[s]; head[s]=tot++; } void addedge(int s,int t) { edge[tt].to=t; edge[tt].next=toph[s]; toph[s]=tt++; } int ti,sn,top,n; int dfn[maxn]; int low[maxn]; int ins[maxn]; int sta[maxn]; int col[maxn]; int ind[maxn]; void tarjan(int u) { dfn[u]=low[u]=++ti; ins[u]=1; sta[++top]=u; int i,k; for(i=head[u];i;i=e[i].next) { k=e[i].to; if(dfn[k]==0) { tarjan(k); low[u]=min(low[u],low[k]); } else if(ins[k]) low[u]=min(low[u],dfn[k]); } if(dfn[u]==low[u]) { sn++; do { k=sta[top--]; ins[k]=0; col[k]=sn; }while(k!=u); } } int q[maxn]; int toposort() { int front,rear; front=rear=0; int i,k; for(i=1;i<=sn;i++) if(ind[i]==0) q[rear++]=i; if(rear!=1) return 0; int num=0; while(front<rear) { int x=q[front++]; int tt=0; num++; for(i=toph[x];i;i=edge[i].next) { k=edge[i].to; ind[k]--; if(ind[k]==0) { q[rear++]=k; tt++; } } if(tt>1) return 0; } if(num==sn) return 1; return 0; } int main() { int t,m,i,j,k; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); clr(head); tot=1; int a,b; while(m--) { scanf("%d%d",&a,&b); add(a,b); } clr(dfn); sn=top=ti=0; for(i=1;i<=n;i++) if(dfn[i]==0) tarjan(i); clr(toph); tt=1; clr(ind); for(i=1;i<=n;i++) for(j=head[i];j;j=e[j].next) { k=e[j].to; if(col[i]!=col[k]) { ind[col[k]]++; addedge(col[i],col[k]); } } printf("%s\n",toposort()?"Yes":"No"); } return 0; }