数据结构(荣誉)实验一 并查集

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上山打老虎D

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值