Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 15720 | Accepted: 4155 |
Description
Input
Output
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
题意:给你一个N个点M条边的有向图,让你判断该图是否为弱连通。
在纸上推了会,终于有所收获。。。
思路:首先tarjan求SCC + 缩点,建成新图后,可以证明的是,新图必定有入度为0的点。在保证每个点都有边相连的前提下,我们进行一次拓扑排序,在这个过程中若遇到不符合弱连通的条件即跳出。反之一直处理到队列为空,这时说明该图为弱连通图。
遵循条件
一:新图不能有多于1个的入度为0的点,这是保证每个点都有边相连。
二:在拓扑排序遍历点u的过程中,若去掉与u相关的边后出现多于1个的入度为0的点,说明这些点只能由u到达,而它们之间不存在可达路径。这时不满足弱连通,跳出。
AC代码:
#include <cstdio> #include <cstring> #include <stack> #include <queue> #include <vector> #include <algorithm> #define MAXN 2000 #define MAXM 8000 using namespace std; struct Edge { int from, to, next; }; Edge edge[MAXM]; int head[MAXN], edgenum; int low[MAXN], dfn[MAXN]; int dfs_clock; int sccno[MAXN], scc_cnt; stack<int> S; bool Instack[MAXN]; int N, M; vector<int> G[MAXN];//存储缩点后新图 int in[MAXN];//记录入度 void init() { edgenum = 0; memset(head, -1, sizeof(head)); } void addEdge(int u, int v) { Edge E = {u, v, head[u]}; edge[edgenum] = E; head[u] = edgenum++; } void getMap() { int x, y; while(M--) { scanf("%d%d", &x, &y); addEdge(x, y); } } void tarjan(int u, int fa) { int v; low[u] = dfn[u] = ++dfs_clock; S.push(u); Instack[u] = true; for(int i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if(!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); } else if(Instack[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) { scc_cnt++; for(;;) { v = S.top(); S.pop(); Instack[v] = false; sccno[v] = scc_cnt; if(v == u) break; } } } void find_cut(int l, int r) { memset(low, 0, sizeof(low)); memset(dfn, 0, sizeof(dfn)); memset(sccno, 0, sizeof(sccno)); memset(Instack, false, sizeof(Instack)); dfs_clock = scc_cnt = 0; for(int i = l; i <= r; i++) if(!dfn[i]) tarjan(i, -1); } void suodian()//缩点 { for(int i = 1; i <= scc_cnt; i++) G[i].clear(), in[i] = 0; for(int i = 0; i < edgenum; i++) { int u = sccno[edge[i].from]; int v = sccno[edge[i].to]; if(u != v) G[u].push_back(v), in[v]++; } } void solve()//拓扑排序 判断 { queue<int> Q; int cnt = 0; for(int i = 1; i <= scc_cnt; i++) { if(in[i] == 0) cnt++, Q.push(i); if(cnt > 1)//两个入度为0的点 必不是弱联通 { printf("No\n"); return ; } } while(!Q.empty()) { int u = Q.front(); Q.pop(); cnt = 0; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(--in[v] == 0) { cnt++; if(cnt > 1)//两个分支 必不是弱连通 { printf("No\n"); return ; } Q.push(v); } } } printf("Yes\n"); } int main() { int t; scanf("%d", &t); while(t--) { scanf("%d%d", &N, &M); init(); getMap(); find_cut(1, N); suodian();//缩点 solve(); } return 0; }