图中的环
图中的环
给定一个 $n$ 个点 $m$ 条边的无向图。
点的编号从 $1$ 到 $n$。
图中不含重边和自环。
请你对给定图进行判断,如果该图是一个有且仅有一个环的连通图,则输出 YES ,否则输出 NO 。
输入格式
第一行包含两个整数 $n,m$。
接下来 $m$ 行,每行包含两个整数 $a,b$,表示点 $a$ 和点 $b$ 之间存在一条无向边。
输出格式
如果该图是一个有且仅有一个环的连通图,则输出 YES ,否则输出 NO 。
数据范围
前三个测试点满足 $1 \leq n \leq 10$。
所有测试点满足 $1 \leq n \leq 100$,$0 \leq m \leq \frac{n \left( n?1 \right)}{2}$,$1 \leq a,b \leq n$。
输入样例1:
6 6 6 3 6 4 5 1 2 5 1 4 5 4
输出样例1:
YES
输入样例2:
6 5 5 6 4 6 3 1 5 1 1 2
输出样例2:
NO
解题思路
图是连通的,而且有且只有一个环,那么可以想象一下,这个图有一个环,环上挂着一堆树。这种图就是基环树。
一个图是基环树的充分必要条件是:图是连通的、边的数量等于点的数量。
因此问题就变成了判断一个图是否为基环树。
首先先判断边数是否等于点数,再判断图是否连通。判断图的连通性可以用并查集或dfs。
并查集做法:
1 #include2 #include 3 using namespace std; 4 5 const int N = 110; 6 7 int fa[N]; 8 9 int find(int x) { 10 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 11 } 12 13 int main() { 14 int n, m; 15 scanf("%d %d", &n, &m); 16 if (m != n) { 17 printf("NO"); 18 return 0; 19 } 20 21 for (int i = 0; i <= n; i++) { 22 fa[i] = i; 23 } 24 25 int cnt = n; 26 while (m--) { 27 int a, b; 28 scanf("%d %d", &a, &b); 29 if (find(a) != find(b)) { 30 fa[find(a)] = find(b); 31 cnt--; 32 } 33 } 34 35 printf("%s", cnt == 1 ? "YES" : "NO"); 36 37 return 0; 38 }
dfs做法:
1 #include2 #include 3 #include 4 using namespace std; 5 6 const int N = 110, M = N << 1; 7 8 int head[N], e[M], ne[M], idx; 9 bool vis[N]; 10 int last = -1; 11 12 void add(int v, int w) { 13 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 14 } 15 16 void dfs(int src, int pre) { 17 vis[src] = true; 18 for (int i = head[src]; i != -1; i = ne[i]) { 19 if (e[i] != pre && !vis[e[i]]) dfs(e[i], src); 20 } 21 } 22 23 int main() { 24 int n, m; 25 scanf("%d %d", &n, &m); 26 if (m != n) { 27 printf("NO"); 28 return 0; 29 } 30 31 memset(head, -1, sizeof(head)); 32 while (m--) { 33 int v, w; 34 scanf("%d %d", &v, &w); 35 add(v, w), add(w, v); 36 } 37 38 dfs(1, -1); 39 40 int cnt = 0; 41 for (int i = 1; i <= n; i++) { 42 if (vis[i]) cnt++; 43 } 44 printf("%s", cnt == n ? "YES" : "NO"); 45 46 return 0; 47 }
参考资料
AcWing 4216. 图中的环(AcWing杯 - 周赛):https://www.acwing.com/video/3690/