1.并查集基础
题目描述
现在有一个并查集,你需要完成合并和查询操作。
输入
第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。
接下来M 行,每行包含三个整数 Zi,Xi,Yi 。
当 Zi=1 时,将 Xi 与 Yi 所在的集合合并。
当Zi=2 时,输出Xi 与 Yi 是否在同一集合内,是的输出 Y ;否则输出 N 。
输出
对于每一个Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N 。
样例输入
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
样例输出
N
Y
N
Y
题解
#include <iostream>
using namespace std;
int main() {
int n, t;
cin >> n >> t;
int* arr = new int[n+10];
for (int i = 0; i < n+1; i++) {
arr[i] = i;
}
while (t--) {
int a, b, c;
cin >> a >> b >> c;
if (a == 1) {
while (arr[b]!=b) {
b = arr[b];
}
while (arr[c] != c){
c = arr[c];
}
arr[c] = b;
}
else if (a == 2) {
while (arr[b] != b) {
b = arr[b];
}
while (arr[c] != c) {
c = arr[c];
}
if (arr[b] == arr[c]) {
cout << "Y" << endl;
}
else {
cout << "N" << endl;
}
}
}
}
2. 红娘
题目描述
小明在A公司工作,小红在B公司工作。A公司有N名男员工,其中有P对有朋友关系。B公司有M名女员工,其中有Q对有朋友关系。假设朋友的朋友还是朋友。
每对朋友关系用两个整数(Xi, Yi)组成,表示朋友的编号分别为Xi,Yi。男人的编号是正数,女人的编号是负数。小明的编号是1,小红的编号是-1.
小明和小红是情侣,他们想撮合A、B公司的员工成为情侣。只有异性男女才能成为情侣,而且任何一方都不能脚踏两只船哦。那么,请问两公司之间,通过小明和小红认识的人最多一共能配成多少对情侣。(包括他们自己)
输入
第1行,4个空格隔开的正整数N,M,P,Q。
之后P行,每行两个正整数, Xi, Yi。
之后Q行,每行两个负整数, Xi, Yi。
输出
一个正整数,表示通过小明和小红认识的人最多一共能配成多少对情侣。(包括他们自己)
样例输入
4 3 4 2
1 1
1 2
2 3
1 3
-1 -2
-3 -3
样例输出
2
题解
#include <algorithm>
#include <iostream>
using namespace std;
class UnionSet
{
private:
int *roots;
int length;
public:
UnionSet(int n)
{
roots = new int[n + 1];
for (int i = 1; i <= n; i++)
{
roots[i] = i;
}
length = n;
}
~UnionSet()
{
delete[] roots;
}
int find(int a)
{
int p = a;
while (roots[p] != p)
{
p = roots[p];
}
return p;
}
void unionMerge(int a, int b)
{
int x = find(a);
int y = find(b);
roots[y] = x;
}
int countRoot()
{
int x = find(1);
int num = 0;
for (int i = 1; i <= length; i++)
{
if (x == find(i))
{
num++;
}
}
return num;
}
};
int main()
{
int n, m, p, q;
cin >> n >> m >> p >> q;
UnionSet male(n);
UnionSet female(n);
while (p--)
{
int x, y;
cin >> x >> y;
male.unionMerge(x, y);
}
while (q--)
{
int x, y;
cin >> x >> y;
female.unionMerge(-x, -y);
}
cout << min(male.countRoot(), female.countRoot()) << endl;
return 0;
}
3.滑雪
题目描述
David是个滑雪初学者,他还没有学会如何在滑行过程中控制方向,也不知道如何停下来。所以他的滑行方式只能是在一个高高的雪堆上调整好向北,东,南或西移动的方向,然后直线滑下来,一直滑倒另一个雪堆底下才能停下来。
他发觉以这种方式,有些雪堆他是无法到达的。因此,他现在想在已有的雪堆基础上,自己再人为地增加一些雪堆,使得他可以从任何雪堆转移到其他任何雪堆。他请求您帮他找到需要创建的雪堆的最少数量。
我们假设David只能在整数坐标处堆积雪堆。
输入
输入的第一行包含一个整数n(1≤n≤100),表示已有的雪堆数量。 接下来的n行中的每行包含两个整数Xi和Yi(1≤Xi,Yi≤1000),表示已有的第i个雪堆的坐标。
请注意,北方向与Oy轴方向一致,因此东方向与Ox轴方向一致。 所有雪堆的位置都不同。
输出
输出为使David能够从任何雪堆到达其他任何雪堆而需要创建的最小雪堆数。
样例输入
2
2 1
1 2
样例输出
1
题解
#include <algorithm>
#include <iostream>
#include <set>
using namespace std;
class UnionSet
{
private:
int *roots;
int length;
public:
UnionSet(int n)
{
roots = new int[n + 1];
for (int i = 1; i <= n; i++)
{
roots[i] = i;
}
length = n;
}
~UnionSet()
{
delete[] roots;
}
int find(int a)
{
if(roots[a] == a)
{
return a;
}else
{
return find(roots[a]);
}
}
void merge(int a, int b)
{
int x = find(a);
int y = find(b);
if (x != y)
{
roots[y] = x;
}
}
int count()
{
set<int> s;
for (int k = 1; k <= length; k++)
{
s.insert(roots[k]);
}
return s.size();
}
};
struct Point
{
int x;
int y;
};
int main()
{
int n;
cin >> n;
UnionSet us(n);
Point *point = new Point[n + 1];
for (int i = 1; i <= n; i++)
{
cin >> point[i].x >> point[i].y;
}
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
if (point[i].x == point[j].x || point[i].y == point[j].y)
{
us.merge(i, j);
}
}
}
cout << us.count() - 1 << endl;
return 0;
}