题意:给定一个有向图,判断此图是否为单向连通(=半连通)。(注意单向连通和弱连通的区别:前者是图中任意两点u和v,或者有uv路或者有vu路;后者是有向图的基图是连通图)
思路:先求出强连通分量,然后判断拓扑排序是否为一。记得算法课讲过一个DAG图是单向连通当且仅当其拓扑排序唯一。据此此题可解。
#include <stdio.h> #include <string.h> #define min(a,b) ((a)<(b)?(a):(b)) #define clr(s,t) memset(s,t,sizeof(s)) #define N 1005 struct edge{ int y,next; }e[6004],et[6003]; int dfn[N],low[N],st[N],inst[N],first[N],strong[N],d[N],g[N][N],q[N]; int n,m,T,top,stop,id,con,num; void init(){ top = stop = id = con = num = 0; clr(first, -1); clr(dfn, -1); clr(inst, 0); clr(g, 0); clr(d, 0); } void add(int x,int y){ e[top].y = y; e[top].next = first[x]; first[x] = top++; } void tarjan(int x){ int i,y; dfn[x] = low[x] = ++id; inst[x] = 1; st[stop++] = x; for(i = first[x];i!=-1;i=e[i].next){ y = e[i].y; if(dfn[y] == -1){ tarjan(y); low[x] = min(low[x],low[y]); }else if(inst[y]) low[x] = min(low[x],dfn[y]); } if(dfn[x] == low[x]){ con++; do{ strong[st[--stop]] = con; inst[st[stop]] = 0; }while(x != st[stop]); } } int main(){ scanf("%d",&T); while(T--){ int i,j,a,b; init(); scanf("%d %d",&n,&m); for(i = 0;i<m;i++){ scanf("%d %d",&a,&b); add(a,b); } for(i = 1;i<=n;i++) if(dfn[i] == -1) tarjan(i); for(i = 1;i<=n;i++) for(j = first[i];j!=-1;j=e[j].next) if(strong[i] != strong[e[j].y]){ d[strong[e[j].y]]++; g[strong[i]][strong[e[j].y]] = 1;//以强连通分量为顶点重新构图 } for(i = 1;i<=con;i++){ for(j = 1;j<=con;j++) if(!d[j]){ q[++num] = j; d[j] = -1; } if(num!=i)//拓扑排序如果唯一,每次只出现一个度为0的点 break; for(j = 1;j<=con;j++) if(g[q[num]][j]) d[j]--; } if(i>con) printf("Yes\n"); else printf("No\n"); } }