P3367 【模板】并查集
基本原理:每个集合用一个树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N = 10010;
int p[N];//表示父节点
int find(int x)// 找到x的祖宗节点 + 把路径上所有点的父节点变为祖宗节点
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i ++) p[i] = i;//初始化,一开始时每个点单独为一个集合
while(m --)
{
int a, b, c;
cin >> a >> b >> c;
if(a == 1)
{
p[find(b)] = find(c);//将b的祖宗节点的父节点变为c的祖宗节点,就将两个集合合并了
}
else
{
if(find(b) == find(c)) puts("Y");
else puts("N");
}
}
return 0;
}
P1551 亲戚
基本原理:每个集合用一个树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。
#include<iostream>
#include<algorithm>
#include<stdio.h>
const int N = 5005;
int q[N];
using namespace std;
int find(int x)
{
if(q[x] != x) q[x] = find(q[x]);
return q[x];
}
int main()
{
int n, m, p;
cin >> n >> m >> p;
for(int i = 1; i <= n; i ++) q[i] = i;//有n个人,最初每个人是单独的,互相没有关系。
while(m --)
{
int a, b;
cin >> a >> b;
q[find(a)] = find(b);//将a的祖宗节点的父节点变为b的祖宗节点,就使之成为一个集合。
}
while(p --)
{
int c, d;
cin >> c >> d;
if(find(c) == find(d)) puts("Yes");//祖宗节点相同
else puts("No");
}
return 0;
}
P2256 一中校运会之百米跑
基本原理:每个集合用一个树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。
唯一难点是字符串的处理,关于字符串处理,c++里有个库函数叫map,map<string, string> p,就相当于定义一个类型为字符串, 下标为字符串的数组p。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
map<string, string> p;
string find(string x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n, m, k;
cin >> n >> m;
while(n --)
{
string x;
cin >> x;
p[x] = x;
}
while(m --)
{
string a, b;
cin >> a >> b;
p[find(a)] = find(b);
}
cin >> k;
while(k --)
{
string c, d;
cin >> c >> d;
if(find(c) == find(d)) puts("Yes.");
else puts("No.");
}
return 0;
}
P1536 村村通
基本原理:每个集合用一个树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。
思路:将用已有道路连接的城镇放在一个集合,然后统计有多少个不同的集合。
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N = 1010;
int p[N], q[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n, m;
while(1)
{
scanf("%d", &n);
if(n == 0) break;
scanf("%d", &m);
for(int i = 1; i <= n; i ++)
{
p[i] = i;
}
while(m --)
{
int x1, x2;
cin >> x1 >> x2;
if(find(x1) != find(x2))
{
p[find(x1)] = find(x2);
}
}
int sum = 0;
for(int i = 1; i <= n; i ++)
{
q[i] = find(i);//将每一个点的祖宗节点重新用一个新的数组存储。
}
//去掉q数组中重复的数
for(int i = 1; i <= n; i ++)
{
for(int j = i + 1; j <= n; j ++)//从i后面的一个元素开始寻找。
{
if(q[j] == q[i])//如果发现重复。
{
for(int k = j + 1; k <= n; k ++)
{
q[k - 1] = q[k];//将后面的数依次赋值给前面一个位置。
}
n --;//数组长度减1.
j --;//重复点再次进行查重。
}
}
}
for(int i = 1; i <= n; i ++)
{
if(q[i] != 0)
sum ++;//统计共有多少个集合。
}
cout << sum - 1 << endl;
}
return 0;
}
P2814 家谱
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<map>
using namespace std;
map<string, string> p;
string find(string x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
string a, b;
char ch;
cin >> ch;
while(ch != '$')
{
cin >> a;
if(ch == '#')
{
b = a;//当每次输入的是#时,b存储的是当前所输入的父亲而不是之前输入的父亲。
if(p[a] == "") p[a] = a;//当a是最早的祖先时,就使p[a]等于自己。
}
else if(ch == '+')
{
p[a] = b;//a的父亲为b。
}
else
{
cout << a << ' ' << find(a) << endl;
}
cin >> ch;
}
return 0;
}
P2078 朋友
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int N = 20010;//n + m的数据范围
int a[N],cnt[N];
int find(int x)
{
if(a[x] != x) a[x] = find(a[x]);
return a[x];
}
int main()
{
int n, m, p, q;
cin >> n >> m >> p >> q;
for(int i = 1; i <= n + m; i ++)//n + 1往后是存储的女生
{
a[i] = i, cnt[i] = 1;
}
while(p --)
{
int x1, x2;
cin >> x1 >> x2;
if(find(x1) != find(x2))
{
cnt[find(x2)] += cnt[find(x1)];
a[find(x1)] = find(x2);
}
}
while(q --)
{
int x1, x2;
cin >> x1 >> x2;
if(find(n - x1) != find(n - x2))//因为x1,x2是负数,用n来减得到存储的下标。
{
cnt[find(n - x2)] += cnt[find(n - x1)];
a[find(n - x1)] = find(n - x2);
}
}
//统计find(1)与find(n + 1)中各自的人数,其中最小的就是结果。
int t = cnt[find(1)], h = cnt[find(n + 1)];
if(t <= h) cout << t;
else cout << h;
return 0;
}
P6121 [USACO16OPEN]Closing the Farm G