AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

2025.5.7 学习笔记(并查集习题)

作者: 作者的头像   jwh ,  2025-05-08 21:49:47 · 安徽 ,  所有人可见 ,  阅读 2


0


第一题: 银河英雄传说

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<string,int> PSI;
const int inf = 0x3f3f3f3f;
const int N = 30010;  // 最大战舰数量

int m;  // 指令总数
int p[N], si[N], d[N];  
// p数组:并查集的父节点数组
// si数组:记录每个集合的大小(战舰数量)
// d数组:记录每个战舰到其所在集合根节点的距离

// 并查集的查找函数,带路径压缩和距离维护
int find(int x)
{
    if (p[x] != x)  // 如果x不是根节点
    {
        int root = find(p[x]);  // 递归找到根节点
        d[x] += d[p[x]];  // 维护x到根节点的距离(累加父节点的距离)
        p[x] = root;  // 路径压缩,直接指向根节点
    }
    return p[x];
}
int main()
{
    scanf("%d", &m);  // 读取指令总数
    // 初始化并查集
    for (int i = 1; i < N; i ++ )
    {
        p[i] = i;  // 每个节点的父节点初始化为自己
        si[i] = 1;  // 每个集合的大小初始化为1
    }
    while (m -- )  // 处理每条指令
    {
        char op[2];  // 操作类型
        int a, b;    // 操作涉及的两个战舰编号
        scanf("%s%d%d", op, &a, &b);

        if (op[0] == 'M')  // 如果是合并指令
        {
            int pa = find(a), pb = find(b);  // 找到两个战舰的根节点
            if (pa != pb)  // 如果不在同一集合才需要合并
            {
                d[pa] = si[pb];  // 将pa集合的根节点到pb集合根节点的距离设为pb集合的大小
                si[pb] += si[pa];  // 更新pb集合的大小
                p[pa] = pb;  // 将pa集合合并到pb集合
            }
        }
        else  // 如果是查询指令
        {
            int pa = find(a), pb = find(b);  // 找到两个战舰的根节点
            if (pa != pb) puts("-1");  // 不在同一集合输出-1
            else 
                // 输出两个战舰之间的距离差减1(即中间的战舰数量)
                printf("%d\n", max(0, abs(d[a] - d[b]) - 1));
        }
    }
    return 0;
}

第二题: 围棋4

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int,int>
const int N=1e5+10;

// 定义四个方向的偏移量:上、下、左、右
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};

// 定义每个棋块的数据结构
struct node {
    int p;       // 父节点(用于并查集)
    int maxx;    // 棋块中棋子的最大x坐标
    int minx;    // 棋块中棋子的最小x坐标
    int maxy;    // 棋块中棋子的最大y坐标
    int miny;    // 棋块中棋子的最小y坐标
    int s;       // 棋块的覆盖值(矩形面积)
};

node a[22*22];  // 棋盘上每个位置对应的棋块数据(19x19的棋盘,用20x20的数组)
int n;          // 棋子数量
int b[22][22];  // 棋盘,记录每个位置的颜色(1黑/2白)
int ans;        // 当前所有棋块的覆盖值总和

// 合并两个棋块的函数
void solve(int id1, int id2) {
    // 更新棋块2的边界信息,取两个棋块的极值
    a[id2].maxx = max(a[id2].maxx, a[id1].maxx);
    a[id2].minx = min(a[id2].minx, a[id1].minx);
    a[id2].maxy = max(a[id2].maxy, a[id1].maxy);
    a[id2].miny = min(a[id2].miny, a[id1].miny);
    // 重新计算合并后的矩形面积
    a[id2].s = (a[id2].maxx - a[id2].minx + 1) * (a[id2].maxy - a[id2].miny + 1);
    // 将棋块1的父节点指向棋块2(合并)
    a[id1].p = id2;
}

// 并查集的查找函数(带路径压缩)
int find(int x) {
    if (a[x].p != x) {
        int root = find(a[x].p);  // 递归查找根节点
        solve(x, root);           // 路径压缩时合并信息
        a[x].p = root;           // 路径压缩
    }
    return a[x].p;
}

int main() {
    cin >> n;  // 读取棋子数量

    for (int i = 1; i <= n; i++) {
        int c, x, y;
        scanf("%d %d %d", &c, &x, &y);  // 读取棋子颜色和坐标

        // 在棋盘上放置当前棋子
        b[x][y] = c;

        // 计算当前棋子的唯一标识(将二维坐标转换为一维)
        int id = x * 20 + y;

        // 初始化当前棋子的棋块信息
        a[id].p = id;     // 父节点指向自己
        a[id].maxx = x;   // 初始化最大x坐标
        a[id].minx = x;    // 初始化最小x坐标
        a[id].maxy = y;   // 初始化最大y坐标
        a[id].miny = y;    // 初始化最小y坐标
        a[id].s = (a[id].maxx - a[id].minx + 1) * (a[id].maxy - a[id].miny + 1);  // 计算初始面积

        // 将当前棋块的面积加入总和
        ans += a[id].s;

        // 检查四个方向的相邻位置
        for (int j = 0; j < 4; j++) {
            int nx = x + dx[j];
            int ny = y + dy[j];

            // 如果相邻位置在棋盘内且颜色相同
            if (b[x][y] == b[nx][ny] && nx >= 1 && ny >= 1 && nx <= 19 && ny <= 19) {
                int idd = nx * 20 + ny;  // 相邻位置的唯一标识

                // 查找当前棋子和相邻棋子的根节点
                int pa = find(id), pb = find(idd);

                // 如果两个棋子不属于同一个棋块
                if (pa != pb) {
                    // 从总和中减去两个棋块的面积
                    ans -= a[pa].s;
                    ans -= a[pb].s;

                    // 合并两个棋块(将pa合并到pb中)
                    solve(pa, pb);
                    a[pa].p = pb;

                    // 将合并后的新面积加入总和
                    ans += a[pb].s;
                }
            }
        }

        // 输出当前所有棋块的覆盖值总和
        printf("%d ", ans);
    }

    return 0;
}

第三题: 森林1
d[x]:以x为根节点的数的最大高度 ,维护d[x]。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int,int>
const int N=1e5+10;
int p[1000010];
int d[1000010];
int n,m;
int ans=-1;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        d[i]=1;
    }
    while(m--)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        int pa=find(a);
        int pb=find(b);
        if(pa!=pb)
        {
            d[pb]=max(d[pb],d[pa]+1);
            p[pa]=pb;
            cout<<d[pb]<<" ";
        }
        else printf("0 ");
    }
    return 0;
}

第四题: 铁索连环(boat)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int,int>
const int N=1e5+10;
int n,m;
int p[1010];
int d[1010];
vector<int> q;
bool st[1010];
int find(int x)
{
    if(x!=p[x])
    {
        st[x]=true;
        int t=find(p[x]);
        p[x]=t;
    }
    return p[x];
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        p[i]=i;
        scanf("%d",&d[i]);
    }
    while(m--)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        int pa=find(a);
        int pb=find(b);
        if(pa!=pb)
        {
            d[pb]+=d[pa];
            p[pa]=pb;
        }
    }
    for(int i=0;i<n;i++)
    {
        if(!st[i]&&p[i]==i)
        {
            q.push_back(d[i]);
        }
    }
    cout<<q.size()<<endl;
    sort(q.begin(),q.end());
    for(int i=0;i<q.size();i++) cout<<q[i]<<" ";

    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息