并查集
1.蓝桥幼儿园(基础并查集)
【题目链接】蓝桥幼儿园 - 蓝桥云课 (lanqiao.cn)
【代码实现】
#include <iostream>
using namespace std;
const int N = 2e6 + 10;
int p[N];
int n, m;
void init()
{
for(int i = 1; i <= n; i ++) p[i] = i;
}
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int a, int b)
{
if(find(a) != find(b))
p[find(a)] = find(b);
}
int main()
{
// 请在此输入您的代码
cin >> n >> m;
init();
while(m --)
{
int a, b, opt;
cin >> opt >> a >> b;
if(opt == 1) merge(a, b);
else
{
if(find(a) != find(b))
puts("NO");
else
puts("YES");
}
}
return 0;
}
2.合根植物(并查集求连通块个数)
【题目链接】合根植物 - 蓝桥云课 (lanqiao.cn)
思路:并查集求连通块个数。
【代码实现】
#include<iostream>
#include<unordered_set>
#include<set>
using namespace std;
const int N = 1e7 + 10;
typedef long long LL;
int p[N];
int a[N], sizes[N];
int n, m;
// 并查集维护有多少个连通块
void init()
{
for(int i = 1; i <= n * m; i ++) p[i] = i;
}
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y)
{
if(find(x) != find(y))
p[find(x)] = find(y);
}
int main()
{
cin >> n >> m;
init();
int k;
cin >> k;
while(k --)
{
int a, b;
cin >> a >> b;
merge(a, b);
}
int sum = 0;
for(int i = 1; i <= n * m; i ++)
if(p[i] == i)
sum ++;
cout << sum;
return 0;
}
3.七段码(建图+并查集 + dfs(指数类型枚举))
【题目链接】七段码 - 蓝桥云课 (lanqiao.cn)
思路:
每个灯只有两者选择,开或者灭(递归组合类型枚举),暴力枚举七个灯的所有开灭情况,然后并查集维护连通性,若果当前状态开的灯都在一个连通块当中话(连通块个数为1),说明此方案符合要求,反之不然。
建图:
abcdefg
节点编号:1234567
a:1–2,1–6
b:2–1,2–3,2–7
c:3–4,3–2,3–7
d:4–3,4–5
e:5–4,5–6,5–7
f:6–5,6–7,6–1,6–7
g:7–2,7–3,7–5,7–6
【代码实现】
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10;
int g[N][N];
bool st[N];
int p[N];
int ans;
void init()
{
g[1][2] = g[1][6] = 1;
g[2][1] = g[2][3] = g[2][7] = 1;
g[3][4] = g[3][2] = g[3][7] = 1;
g[4][3] = g[4][5] = 1;
g[5][4] = g[5][6] = g[5][7] = 1;
g[6][1] = g[6][5] = g[6][7] = 1;
g[7][2] = g[7][3] = g[7][5] = g[7][6] = 1;
}
int find(int x) // 并查集
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int a, int b)
{
if(find(a) != find(b))
p[find(a)] = find(b);
}
void dfs(int u)
{
if(u == 8)// 枚举完七盏灯的情况
{
// 注意:因此有多组状态要判断,每次判断时p[]数组要重置!!!
for (int i = 1; i <= 7; i ++ ) p[i] = i;
// 将7盏灯中 开的合并
for (int i = 1; i <= 7; i ++ )
for (int j = 1; j <= 7; j ++ )
if(g[i][j] && st[i] && st[j])// 节点i --- 节点j(亮的灯合并)
merge(i, j);
// 判断亮的灯的连通块的个数
int cnt = 0;
for(int i = 1; i <= 7; i ++)
if(st[i] && p[i] == i) cnt ++;
if(cnt == 1)// 说明只有一个连通块(亮的灯都连通了)
ans ++;
return ;
}
// u这栈灯有亮与不亮两种选择
// 亮
st[u] = true;
dfs(u + 1);
// 不亮
st[u] = false;
dfs(u + 1);
}
int main()
{
init();
dfs(1);// 从第一栈灯开始
cout << ans;
return 0;
}
4.发现环(并查集判环+dfs图的遍历)
【题目链接】发现环 - 蓝桥云课 (lanqiao.cn)
并查集判断环,当出现环时,令其中一点为起点,另外一点为终点,dfs遍历记录路径。
【代码实现】
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int p[N], ans[N];
int h[N], e[N], ne[N], idx;
bool st[N];
int n;
int S, T;
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void init()
{
memset(st, 0, sizeof st);
memset(h, -1, sizeof h);
for(int i = 1; i <= n; i ++) p[i] = i;
}
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y)
{
if(find(x) != find(y))
p[find(x)] = find(y);
}
void dfs(int now, int u)
{
st[now] = true;
ans[u] = now;
if(now == T)
{
sort(ans, ans + u + 1);
for(int i = 0; i <= u; i ++) cout << ans[i] << ' ';
return ;
}
for(int i = h[now]; i != -1; i = ne[i])
{
int j = e[i];
if(!st[j])
dfs(j, u + 1);
}
}
int main()
{
// 请在此输入您的代码
cin >> n;
init();
for(int i = 1; i <= n; i ++)
{
int a, b;
cin >> a >> b;
if(find(a) == find(b))
{
S = a, T = b;
}
else
{
// 无向图
add(a, b);
add(b, a);
merge(a, b);
}
}
dfs(S, 0);
return 0;
}