并查集 L2-048

并查集是一种用于处理不交集问题的数据结构,以及一些图的连通操作
find 操作 →O(1)\rightarrow O(1)O(1)
union 操作 O(n  log  n)O(n\;log\;n)O(nlogn)

L2-048

编码

对地图中的每一组 (i,j)(i,j)(i,j),可以不重复地表示为 i×m+ji\times m+ji×m+j 的形式。

解码

同样地,对一个 i×m+ji\times m+ji×m+j ,也可以得到唯一的
{i=vmj=i mod m\begin{cases}i=\frac{v}{m}\\j=i\bmod m\end{cases}{i=mvj=imodm
至此,我们可以使用一个数 nnn 来唯一表示地图矩阵中的一点,这么做是为了避免在并查集中使用 pair 存储点坐标。

总体思路

通过并查集表示各个岛屿,并且在处理并查集的过程中同步记录各个连通块中的最大值。最后遍历各个并查集的内容确定岛屿和宝藏数量。
为了避免超时,需要使用路径压缩优化查询的时间复杂度。

偏移量搜索周围元素

定义偏移量数组 dx={−1,0,1,0},dy={0,1,0,1}d_x=\{-1,0,1,0\},d_y=\{0,1,0,1\}dx={1,0,1,0},dy={0,1,0,1},表示上下左右四个方向。
|300
注意在循环更新坐标的时候要检查是否越界。

路径压缩
int find(int u){//路径压缩
    return u == p[u] ? p[u] : (p[u] = find(p[u]));
}

这个就是检查当前值是否等于特征节点的值,如果是,则将 u 设为 p[u],中断递归,否则将其设置为其父节点(也就是 p[u] 的值),向上搜索直到根节点为止。
这个 find 函数最终会返回根节点的值,再将根节点和当前节点直接连通即可。

#include <bits/stdc++.h>
using namespace std;

vector<int> p;
vector<char> mx;//宝藏

int find(int u){//路径压缩
    return u == p[u] ? p[u] : (p[u] = find(p[u]));
}

int n,m;
vector<string> g;
int dx[] = {-1,0,1,0};
int dy[] = {0,1,0,-1};

int main(){
    cin >> n >> m;
    g.resize(n);//地图
    p.resize(n*m);//标记地图上每个岛屿点的根节点
    mx.resize(n*m);//把岛屿的父节点标记成宝藏的值
    

    for(auto &s : g) cin >> s;//读入地图
    for(int i = 0;i<n*m;i++){
        p[i] = i;
        mx[i] = g[i / m][i % m];//根绝地图特征值确定坐标,mx如果
    }//初始化并查集,令其特征节点均为自身
    for(int i = 0;i<n;i++){
        for(int j = 0;j<m;j++){
            if(g[i][j] == '0'){
                continue;
            }
            //陆地上下左右合并联通块
            int u = i*m + j;//确定坐标特征值
            for(int k = 0;k<4;k++){
                int ni = i + dx[k], nj = j + dy[k];
                if(ni < 0 || ni >= n || nj < 0 || nj >= m) continue;//越界
                int v = ni*m + nj;//当前检查的坐标的特征值
                if(g[ni][nj] != '0'){
                    u = find(u);//找该节点所在路径上的根节点,压缩当前路径
                    v = find(v);
                    if(u != v){//还未合并入当前连通块,合并之
                        p[u] = v;
                        mx[v] = max(mx[u], mx[v]);//标记宝藏节点
                    }
                }
            }
        }
    }

    int num_cc = 0,num_t = 0;
    for(int i = 0;i<n*m;i++){
        if(p[i] == i){
            int x = i / m;
            int y = i % m;
            if(g[x][y] != '0'){
                num_cc++;
                if(mx[i] > '1'){
                    num_t++;
                }
            }
        }
    }
    cout << num_cc << " " << num_t;
}
### L2-030 并查集 实现及应用 #### 什么是并查集并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并与查询问题。它支持两种主要操作: 1. **查找**:确定某个元素属于哪个子集。这可以通过找到该元素所在子集的一个代表来完成。 2. **合并**:将两个不同的子集合并成一个新的子集。 通过路径压缩和按秩合并优化,并查集可以达到几乎线性的复杂度 \(O(\alpha(n))\) ,其中 \(\alpha(n)\) 是反阿克曼函数,在实际应用中可视为常数时间[^4]。 --- #### 解决L2-030问题的核心思路 假设题目涉及多个家庭成员及其房产分布情况,则需要统计每个家庭的整体人口数量以及人均房产面积等指标。这类问题通常可以用并查集解决,因为我们需要动态维护一组数据间的连通性关系。 具体来说: - 将每个人看作节点。 - 如果两个人有亲属关系,则认为他们处于同一家庭(即同属一个集合)。因此可通过并查集快速判断两人是否在同一家庭,并在此基础上更新统计数据。 最终目标是对所有人的信息进行遍历,利用并查集高效地计算出各个家庭的信息汇总结果[^5]。 --- #### 并查集基本实现代码 以下是基于Python语言的标准并查集模板: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [1] * n def find(self, x): # 查找根结点,带路径压缩 if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): # 按秩合并 rootX = self.find(x) rootY = self.find(y) if rootX != rootY: if self.rank[rootX] > self.rank[rootY]: self.parent[rootY] = rootX elif self.rank[rootX] < self.rank[rootY]: self.parent[rootX] = rootY else: self.parent[rootY] = rootX self.rank[rootX] += 1 ``` 上述代码定义了一个`UnionFind`类,初始化时给定了n个独立节点;提供了`find()`方法用来寻找某节点所属集合的根节点,同时进行了路径压缩优化;还实现了`union()`方法以连接两个不同集合中的任意两点。 --- #### 应用到L2-030的具体步骤 假定输入形式如下: - 输入第一行为整数N表示总人数; - 接下来若干行每行给出一对亲戚关系a b (意味着a和b互为家人),直到结束标志为止; - 最后几行提供每个人的个人资产详情(如房屋数目、总面积等等卡顿)。 按照此逻辑编写程序流程大致如下: 1. 初始化大小为N+1的并查集实例对象uf。 2. 对于每一组亲戚关系(a,b),调用uf.union(a,b)将其加入相同家族之中。 3. 构建辅助字典记录各户人家基本信息比如成员列表、房子总数、覆盖区域平方米数值之类的内容。 4. 输出整理后的答案——即针对每一个单独家族输出相应统计量值。 下面展示一段伪代码片段帮助理解整个过程: ```python from collections import defaultdict def solve(): N = int(input()) # 总人数 uf = UnionFind(N + 1) # 创建并查集 while True: # 处理亲戚关系 try: a, b = map(int, input().split()) uf.union(a, b) except EOFError: break family_info = defaultdict(lambda: {'members': set(), 'houses': 0, 'area': 0}) for i in range(1, N + 1): root = uf.find(i) family_info[root]['members'].add(i) M = int(input()) # 房产条目数 for _ in range(M): person_id, house_count, area_size = map(float, input().split()) root = uf.find(int(person_id)) family_info[root]['houses'] += house_count family_info[root]['area'] += area_size results = [] for info in family_info.values(): member_count = len(info['members']) avg_area_per_person = info['area'] / member_count if member_count > 0 else 0 results.append((member_count, round(avg_area_per_person, 2), info['houses'])) results.sort() for res in results: print(*res) ``` 以上脚本完成了从读取原始资料直至打印最后成果全过程自动化处理工作流设计[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

yyt363045841

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

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

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

打赏作者

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

抵扣说明:

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

余额充值