并查集是一种非常优美的数据结构,并查集是用来解决一些不交集的合并以及查询问题。
并查集模板
int get(int a)
{
if(a==f[a])
return a;
return f[a] = get(f[a]);
}
void merge(int a,int b)
{
a = get(a);
b = get(b);
if(a!=b)
{
f[a] = b;
}
}
How Many Tables
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213
题意:认识的人可以做一个桌子,可以直接认识也可以间接认识,问你最少需要几张桌子。
题解:比较裸的一个并查集题,直接使用模板即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
int f[maxn];
int n, m;
void init()
{
for (int i = 1; i <= n;i++)
{
f[i] = i;
}
}
int get(int a)
{
if(a==f[a])
return a;
return f[a]=get(f[a]);
}
int main()
{
int t;
cin >> t;
while(t--)
{
cin >> n >> m;
init();
for (int i = 1; i <= m;i++)
{
int a, b;
cin >> a >> b;
a = get(a);
b = get(b);
if(a!=b)
f[a] = b;
}
int ans = 0;
for (int i = 1; i <= n;i++)
{
if(f[i]==i)
ans++;
}
cout << ans << endl;
}
return 0;
}
程序自动分析
链接:https://ac.nowcoder.com/acm/problem/17881
题解:这个题也算是一个比较裸的并查集,不过唯一需要的是注意数据范围,这个数据数组肯定开不下,这里我们就需要map来帮助我们了,然后用上板子可以很轻松的写题。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
map<ll, int> m;
ll x[maxn];
ll y[maxn];
ll c[maxn];
int f[maxn * 3];
int get(int a)
{
if(a==f[a])
return a;
return f[a] = get(f[a]);
}
void merge(int a,int b)
{
a = get(a);
b = get(b);
if(a!=b)
{
f[a] = b;
}
}
void init()
{
for (int i = 0; i < maxn * 3;i++)
{
f[i] = i;
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
init();
int n;
scanf("%d", &n);
int cnt = 1;
m.clear();
for (int i = 1; i <= n;i++)
{
scanf("%lld %lld %d", &x[i], &y[i], &c[i]);
if(!m[x[i]])
{
m[x[i]] = cnt++;
}
if(!m[y[i]])
{
m[y[i]] = cnt++;
}
if(c[i]==1)
{
merge(m[x[i]], m[y[i]]);
}
}
int f1 = 1;
for (int i = 1; i <= n;i++)
{
if(c[i]==0)
{
if(get(m[x[i]])==get(m[y[i]]))
{
f1 = 0;
}
}
}
if(f1)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
带权并查集
带权并查集就是在并查集的基础上新定义了一种权值,我们在维护并查集的同时也要维护这个权值,它可以帮助我们方便解决很多问题。
模板
int get(int a)
{
if(a==f[a])
return a;
int root = get(f[a]);
d[a] += d[f[a]];
return f[a] = root;
}
void merge(int a,int b)
{
a = get(a);
b = get(b);
if(a!=b)
{
f[a] = b;
d[a] = size[b];
size[b] += size[a];
}
}
Cube Stacking
题目链接:http://poj.org/problem?id=1988
题解:这个算是带权并查集的板子题,唯一需要注意的点是我们在查询是还需要进行get操作一次,因为这个点以下的哪些点可能没有更新,如果不get一次,很有可能会wa掉。
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 3e4 + 5;
int f[maxn];
int s[maxn];
int d[maxn];
int n;
int get(int a)
{
if(a==f[a])
return a;
int root = get(f[a]);
d[a] += d[f[a]];
return f[a] = root;
}
void init()
{
for (int i = 0; i < maxn; i++)
{
f[i] = i;
s[i] = 1;
}
}
int main()
{
cin >> n;
init();
for (int i = 1; i <= n;i++)
{
char z;
cin >> z;
if(z=='M')
{
int a, b;
cin >> a >> b;
a = get(a);
b = get(b);
if(a!=b)
{
f[a] = b;
d[a] += s[b];
s[b] += s[a];
//s[a] = 0;
}
}
else
{
int a;
get(a);
cout << d[a] << endl;
}
}
return 0;
}
银河英雄传说
链接:https://ac.nowcoder.com/acm/problem/16889
题解:这个题和上个题唯一的不同就是我们需要判断下这两个点是不是在一个队列上,在判断时我们已经进行过get操作了,所以就不需要再进行get操作了,然后输出间距即可。
#include<algorithm>
#include<iostream>
#include<queue>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#include<cmath>
#define ll long long
#define pr pair<ll,int>
using namespace std;
const int maxn = 3e5 + 5;
int f[maxn];
int d[maxn];
int size[maxn];
int get(int a)
{
if(a==f[a])
return a;
int root = get(f[a]);
d[a] += d[f[a]];
return f[a] = root;
}
void merge(int a,int b)
{
a = get(a);
b = get(b);
if(a!=b)
{
f[a] = b;
d[a] = size[b];
size[b] += size[a];
}
}
void init()
{
for (int i = 1; i <= maxn;i++)
{
f[i] = i;
size[i] = 1;
}
}
int main()
{
int n;
cin >> n;
init();
for (int i = 1; i <= n;i++)
{
char s;
cin >> s;
if(s=='M')
{
int a, b;
cin >> a >> b;
merge(b, a);
}
else
{
int a, b;
cin >> a >> b;
if(get(a)==get(b))
{
cout << abs(d[a] - d[b]) - 1 << endl;
}
else
cout << -1 << endl;
}
}
return 0;
}