poj 2762
/* 强连通+缩点+搜索特殊处理,求单向连通 这一题的符合条件的图经过缩点重构后为拓扑图。 对于重新构造出的新图,判断这个图是否单向连通 若结点的后继大于等于2个,则需判断这些所有的后继是之间 否为单向连通,然后搜索所有点。 从中找到的规律为: 入度为0的点有且仅有一个,删除这个点后的图中,入度为0的点依然有且仅有一个 因此只需判断入度为0的结点个数即可判断是否单向连通 */ #include <stdio.h> #include <string.h> #include <vector> #include <stack> using namespace std; vector<int>G1[1005]; vector<int>G2[1005]; stack<int>S; int index,bcnt,flag; int dfn[1005],low[1005],belong[1005],instack[1005],indegree[1005]; void tarjan(int i) { int u,j; S.push(i); dfn[i] = low[i] = ++index; instack[i] = 1; for(j=0; j<G1[i].size(); j++) { u = G1[i][j]; if (!dfn[u]) { tarjan(u); low[i] = min(low[u],low[i]); } else if (instack[u]) low[i] = min(dfn[u],low[i]); } if (dfn[i] == low[i]) { bcnt++; do { u = S.top(); belong[u] = bcnt; S.pop(); instack[u] = 0; } while (u != i); } } void search(int i) { int j,t,sum = 0; for(j=0; j<G2[i].size(); j++) { indegree[G2[i][j]]--;//去除根结点i后,i的所有后继入度减1 if (indegree[G2[i][j]] == 0)//找出根结点 { t = G2[i][j]; sum++; } } if (sum > 1) { flag = 0; return ; } else if (sum == 1) { search(t); } } int main(void) { int T,n,m,a,b,i,j,k,t,sum; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); for(i=1; i<=n; i++) { G1[i].clear(); G2[i].clear(); } while (m--) { scanf("%d%d",&a,&b); G1[a].push_back(b); } index = bcnt = 0; memset(dfn,0,sizeof(dfn)); for(i=1; i<=n; i++) if (!dfn[i]) tarjan(i); memset(indegree,0,sizeof(indegree)); for(i=1; i<=bcnt; i++) for(j=1; j<=n; j++) if (belong[j] == i) for(k=0; k<G1[j].size(); k++) if (i != belong[G1[j][k]]) { G2[i].push_back(belong[G1[j][k]]); indegree[belong[G1[j][k]]]++;//记录结点入度 } flag = 1; sum = 0; for(i=1; i<=bcnt; i++) { if (indegree[i] == 0)//入度为0的为根结点,从这个点开始搜索 { t = i; sum++; } } if (sum > 1)//多于1个则不符合 flag = 0; else search(t); if (flag) printf("Yes\n"); else printf("No\n"); } return 0; }