## 图的连通性判断 #### 总结 - 常用的判连通办法有四种,分别是并查集、$dfs$、$bfs$、$floyd$ - 最常用的是$dfs$、并查集 - 前三种适合稀疏图,$floyd$适合稠密图 ### 一、并查集 ```cpp {.line-numbers} #include using namespace std; const int N = 1010; // 图的最大点数量 /** 共提供两组数据,样例1为不连通用例,样例2为连通用例 样例1:不连通,5号结点为独立的 5 4 1 2 2 3 3 4 1 4 样例2:连通,不存在独立结点 5 4 1 2 2 3 3 4 1 5 检测各种算法是否能准确获取结果 */ int n; // n个人 int m; // m个亲戚 int x, y; // 输入两个人之间的关系 int p[N]; // 并查集数组 // 要深入理解这个递归并压缩的过程 int find(int x) { if (p[x] != x) // 如果x不是族长,递归找父亲,副产品就是找回的结果更新掉自己的家族信息。 p[x] = find(p[x]); // 非常经典的更新,路径压缩大法! // 返回族长是谁 return p[x]; } int cnt; int main() { #ifndef ONLINE_JUDGE // freopen("BCJ1.in", "r", stdin); freopen("BCJ2.in", "r", stdin); #endif // n个人员,m个关系 cin >> n >> m; // 并查集初始化 for (int i = 1; i <= n; i++) p[i] = i; // 自己是自己的老大 // 录入m种关系,使用并查集来判断图的连通性 for (int i = 1; i <= m; i++) { cin >> x >> y; // 加入并查集 int px = find(x), py = find(y); if (px != py) p[px] = py; } // 图已经搭好了,接下来看它们根节点是否相同,如只有一个相同的根节点,则说明是一个连通图 for (int i = 1; i <= n; i++) if (p[i] == i) cnt++; if (cnt == 1) printf("图是连通的\n"); else printf("图不是连通的\n"); return 0; } ``` ### 二、$dfs$ ```cpp {.line-numbers} #include using namespace std; const int N = 1010, M = N << 2; // 图的最大点数量 // 链式前向星 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } int n, m; // 表示图中有n个点,m条边 bool st[N]; int cnt; // 深度遍历 void dfs(int u) { st[u] = 1; cnt++; // 多走了一个结点 for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (!st[v]) dfs(v); } } int main() { #ifndef ONLINE_JUDGE freopen("in1.in", "r", stdin); // freopen("in2.in", "r", stdin); #endif // 初始化链式前向星 memset(h, -1, sizeof h); // 采用邻接表建图 cin >> n >> m; // m条边 for (int i = 1; i <= m; i++) { int a, b; cin >> a >> b; add(a, b); } // 利用dfs进行检查是不是强连通的 dfs(1); if (cnt == n) printf("图是连通的\n"); else printf("图不是连通的\n"); return 0; } ``` ### 三、$bfs$ ```cpp {.line-numbers} #include using namespace std; const int N = 1010, M = N << 2; // 图的最大点数量 int n, m; // 表示图中有n个点,m条边 bool st[N]; int cnt; // 链式前向星 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } int main() { #ifndef ONLINE_JUDGE freopen("in1.in", "r", stdin); // freopen("in2.in", "r", stdin); #endif // 初始化链式前向星 memset(h, -1, sizeof h); // 采用邻接表建图 cin >> n >> m; // m条边 for (int i = 1; i <= m; i++) { int a, b; cin >> a >> b; add(a, b); } // 利用bfs进行检查是不是强连通的 // 把1号结点放入队列 queue q; q.push(1); while (q.size()) { int u = q.front(); q.pop(); st[u] = 1; cnt++; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (!st[v]) q.push(v); } } if (cnt == n) printf("图是连通的\n"); else printf("图不是连通的\n"); return 0; } ``` ### 四、$floyd$ ```cpp {.line-numbers} #include using namespace std; const int N = 1010; // 图的最大点数量 int n, m; // 用floyd来判断起点是否可以达到终点 int g[N][N]; // 邻接矩阵 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) g[i][j] = g[i][j] || (g[i][k] && g[k][j]); } int main() { #ifndef ONLINE_JUDGE freopen("in1.in", "r", stdin); // freopen("in2.in", "r", stdin); #endif // 采用邻接矩阵建图 cin >> n >> m; // m条边 for (int i = 1; i <= m; i++) { int a, b; cin >> a >> b; // 双向建边 g[a][b] = 1, g[b][a] = 1; } // 调用floyd floyd(); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (!g[i][j]) { printf("图不是连通的\n"); cout << i << " " << j << endl; exit(0); } printf("图是连通的\n"); return 0; } ```