并查集
并查集
基本概念
并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。(摘自百度百科)
基本操作
初始化
将每个结点的父结点初始化为自己, 此时有 \(n\) 个集合, 每个集合内有 \(1\) 个元素。
根据题目要求, 并查集内可能维护不同内容, 需要初始化不同内容。
for (int i = 1; i <= n; i ++ )
fa[i] = i;
查询
查询当前结点所在集合的父结点(包含路径压缩)。
注意可能根据题目要求可能在查询时需要更新其它内容。
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
合并
将\(a\)、\(b\)所在集合合并
在合并时可能也需要再更新一些其它信息
int aa = find(a);
int bb = find(b);
if (aa != bb){
fa[aa] = bb;
}
并查集简单应用
AcWing 836. 合并集合
算法思路
并查集模板
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int fa[N];
int n,m;
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
fa[i] = i;
char op[2];
int a, b;
while (m -- ){
scanf("%s%d%d",op, &a, &b);
int aa = find(a);
int bb = find(b);
if (op[0] == 'M'){
if (aa != bb)
fa[aa] = bb;
}else{
if (aa == bb)
puts("Yes");
else
puts("No");
}
}
return 0;
}
AcWing 837. 连通块中点的数量
算法思路
并查集需要维护其集合内的点的数量, 注意每个集合内结点数量这个信息只维护在根节点上, 即只有根节点维护的信息是正确的。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int fa[N];
int sizes[N];
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ){
fa[i] = i;
sizes[i] = 1;
}
char op[2];
int a, b, x;
while (m --){
scanf("%s",op);
if (op[0] == 'C'){
cin >> a >> b;
int aa = find(a);
int bb = find(b);
if (aa == bb)
continue;
fa[aa] = bb;
sizes[bb] += sizes[aa];
}else{
if (op[1] == '1'){
cin >> a >> b;
int aa = find(a);
int bb = find(b);
if (aa == bb)
puts("Yes");
else
puts("No");
}else{
cin >> x;
cout << sizes[find(x)] << endl;
}
}
}
return 0;
}
AcWing 240. 食物链
并查集的综合应用
AcWing 1250. 格子游戏
算法思路
每个格点初始化为一个单独集合, 每次将两个格点连接时, 即将两个集合合并,注意将二维坐标转化为一维
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200 * 200 + 10;
int g[210][210];
int fa[N];
int n, m;
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n * n; i ++ )
fa[i] = i;
for (int i = 1, t = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
g[i][j] = t ++;
int a, b;
string op;
for (int i = 1; i <= m; i ++ ){
cin >> a >> b >> op;
//cout << a << b << op << endl;
if (op[0] == 'D' || op[1] == 'D'){
int aa = find(g[a][b]);
int bb = find(g[a + 1][b]);
if (aa == bb){
cout << i << endl;
return 0;
}else{
fa[aa] = bb;
}
}else{
int aa = find(g[a][b]);
int bb = find(g[a][b + 1]);
if (aa == bb){
cout << i << endl;
return 0;
}else{
fa[aa] = bb;
}
}
}
puts("draw");
return 0;
}
AcWing 1252. 搭配购买
算法思路
先运用并查集将需要捆绑销售的物品合并, 同时维护集合重量和价值之和, 每个集合看作一个新物品, 运用\(01\)背包, 求出最大价值。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int fa[N];
int w[N];
int v[N];
int n, m, W;
int dp[N];
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d%d%d",&n, &m, &W);
for (int i = 1; i <= n; i ++ ){
fa[i] = i;
scanf("%d%d",&w[i], &v[i]);
}
while (m -- ){
int a, b;
scanf("%d%d",&a,&b);
int aa = find(a);
int bb = find(b);
if (aa != bb){
fa[aa] = bb;
w[bb] += w[aa];
v[bb] += v[aa];
}
}
for (int i = 1; i <= n; i ++ )
if (fa[i] == i){
for (int j = W; j >= w[i]; j -- )
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
cout << dp[W] << endl;
return 0;
}
AcWing 237. 程序自动分析
算法思路
先离散化(数据范围太大), 再用并查集。
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e6 + 10;
vector<int> arrays;
vector< pair<PII, int> > changes;
int fa[N];
int n;
int findfa(int x)
{
if (x != fa[x])
fa[x] = findfa(fa[x]);
return fa[x];
}
int find(int x)
{
int l = 0;
int r = arrays.size() - 1;
while (l < r){
int mid = l + r >> 1;
if (arrays[mid] >= x)
r = mid;
else
l = mid + 1;
}
return r + 1;
}
void solve()
{
arrays.clear();
changes.clear();
scanf("%d",&n);
memset (fa, 0, sizeof fa);
for (int i = 1; i <= n; i ++ ){
int a, b, e;
cin >> a >> b >> e;
PII t = {a, b};
changes.push_back({t, e});
arrays.push_back(a);
arrays.push_back(b);
}
sort(arrays.begin(), arrays.end());
arrays.erase(unique(arrays.begin(), arrays.end()), arrays.end());
for (auto it : arrays){
int t = find(it);
//cout << it << "->" << t << endl;
fa[t] = t;
//cout << t << "-->" << fa[t] << endl;
}
for (auto it : changes){
auto t = it.first;
int op = it.second;
//cout << "op->" << op << endl;
int a = find(t.first);
int b = find(t.second);
//cout << a << ' ' << b << endl;
int aa = findfa(a);
int bb = findfa(b);
//cout << aa << ' ' << bb << endl;
if (op == 1 && aa != bb)
fa[aa] = bb;
}
for (auto it : changes){
auto t = it.first;
int op = it.second;
//cout << "op->" << op << endl;
int a = find(t.first);
int b = find(t.second);
//cout << a << ' ' << b << endl;
int aa = findfa(a);
int bb = findfa(b);
//cout << aa << ' ' << bb << endl;
if (op == 0 && aa == bb){
puts("NO");
return;
}
}
puts("YES");
return;
}
int main()
{
int t;
cin >> t;
while (t -- )
solve();
return 0;
}