题目概况
链接: https://nanti.jisuanke.com/t/T1260
难度: 普及/提高-(计蒜客评级普及T3,个人评价T2)
题目分析
简化题目: 有几群人,分别信仰不同的宗教,有几群人(细读题目就能发现)
涉及知识点: 图论、连通块、并查集
解题思路:
1. 题目告诉了我们那两人之间信仰宗教相同,我们可以借此建图(可行,比如A和B相同,B和C相同,那A和C就间接相同,等量代换)
2. DFS查询连通块数量即可
代码要点拆解
输入、建图属于预备知识,不讲
DFS查询连通块
针对DFS本身,我们要用vis
数组记录,经过一个记一个,以免重复遍历
void dfs(int p) {
vis[p] = true; //记录
for (int i = 0; i < G[p].size(); i++) {
int v = G[p][i]; //与p相连的每个点
if (!vis[v]) { //避免重复
dfs(v); //继续遍历相连点
}
}
}
在主函数里需要这样操作:
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
ans++; //如果我一个dfs都完了你这个点还没遍历,那肯定属于另一群人,ans加1
dfs(i); //dfs一遍这个点
}
}
收尾工作和数据的清除
由于我们一次评测有多组数据,前一次用的数据肯定得清除,回想一下我们用了哪些变量是要回收的?
1. vis数组
2. 图
3. ans
cout << "Case " << ++indexx << ": " << ans << endl; //输出按格式
for (int i = 1; i <= n; i++ ){
vector<int>().swap(G[i]);
} //swap大法让这个图释放内存,clear无效;这是个板子,可以记一下
memset(vis, 0, sizeof(vis)); //memset函数yyds!
ans = 0; //ans清零还能用
swap大法的详细讲解:https://blog.csdn.net/m0_37251750/article/details/100071023
完整代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define asn ans //懒得改错,一劳永逸
using namespace std;
const int MAXN = 50002;
vector<int> G[MAXN];
bool vis[MAXN];
int n, m;
int ans, indexx;
void dfs(int p) {
vis[p] = true;
for (int i = 0; i < G[p].size(); i++) {
int v = G[p][i];
if (!vis[v]) {
dfs(v);
}
}
}
int main() {
while (1) {
cin >> n >> m;
if (n == 0 && m == 0) {
break;
}
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
} //建图
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
ans++;
dfs(i);
}
}
cout << "Case " << ++indexx << ": " << ans << endl;
for (int i = 1; i <= n; i++ ){
vector<int>().swap(G[i]);
}
memset(vis, 0, sizeof(vis));
ans = 0;
}
return 0;
}
其他
本题笔者的第一反应就是DFS连通块,而且实现起来比并查集简单,如果大家有兴趣,可以尝试使用并查集解题(实现原理和连通块差不了多少)
并查集讲解:https://oi-wiki.org/ds/dsu/