【题目链接】
ybt 4149:【GESP2509七级】连通图
洛谷 P14077 [GESP202509 七级] 连通图
【题目难度】:E
【题目考点】
1. 图论:连通分量
连通分量:无向图中极大的连通子图。
求图中连通分量数量的方法:
- 深搜广搜
- 并查集
2. 并查集
【解题思路】
本题可以作为求连通分量的模板题。
将图中每个连通分量可以看作一个顶点(这是"缩点"思路),设有 c n t cnt cnt个顶点,那么要想让这 c n t cnt cnt个顶点之间互相连通,形成一个连通图,需要添加的边数最少为 c n t − 1 cnt-1 cnt−1,形成树型结构(无环连通无向图)。
问题变为求图中连通分量的数量 c n t cnt cnt。
解法1:深搜广搜
尝试从每个未访问过的顶点出发进行深搜或广搜,标记已访问的顶点。成功开始搜索的次数为连通分量的数量
解法2:并查集
将每个连通分量当做一个集合。如果顶点A到顶点B有一条无向边,则顶点A、B在同一个集合中,将A、B所在的集合合并。遍历所有的边后,统计集合的数量即为连通分量的数量。
【题解代码】
解法1:深搜广搜求连通分量
- 写法1:深搜求连通分量
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
vector<int> g[N];
int n, m, cnt;
bool vis[N];
void dfs(int u)
{
vis[u] = true;
for(int v : g[u]) if(!vis[v])
dfs(v);
}
int main()
{
int u, v;
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1; i <= n; ++i) if(!vis[i])
{
dfs(i);
cnt++;
}
cout << cnt-1;
return 0;
}
- 写法2:广搜求连通分量
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
vector<int> g[N];
int n, m, cnt;
bool vis[N];
void bfs(int sv)
{
queue<int> que;
vis[sv] = true;
que.push(sv);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int v : g[u]) if(!vis[v])
{
vis[v] = true;
que.push(v);
}
}
}
int main()
{
int u, v;
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = 1; i <= n; ++i) if(!vis[i])
{
bfs(i);
cnt++;
}
cout << cnt-1;
return 0;
}
解法2:并查集求连通分量
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, cnt, fa[N];
void init(int n)
{
for(int i = 1; i <= n; ++i)
fa[i] = i;
}
int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
fa[find(x)] = find(y);
}
int main()
{
int u, v;
cin >> n >> m;
init(n);
for(int i = 1; i <= m; ++i)
{
cin >> u >> v;
merge(u, v);
}
for(int i = 1; i <= n; ++i) if(fa[i] == i)
cnt++;
cout << cnt-1;
return 0;
}