并查集:并与查的结合
简介
并查集是一种用于处理不相交集合的合并和查询操作的数据结构。它是一种树型结构,每个元素都指向其所在集合的根节点。并查集支持以下两种操作:
- 合并: 将两个不相交的集合合并为一个集合。
- 查询: 查询两个元素是否属于同一个集合。
特点
并查集具有以下特点:
- 并查集是一种高效的数据结构,支持快速合并和查询操作。
- 并查集可以用于解决许多与集合相关的编程问题
运用场景
并查集在许多应用场景中都非常有用,例如:
- 最小生成树算法
- 朋友圈问题
- 并查集可以用于解决许多与集合相关的动态问题
例题P3367 【模板】并查集
https://www.luogu.com.cn/problem/P3367
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,M,表示共有 N 个元素和 M个操作。
接下来M 行,每行包含三个整数 Z_i,X_i,Y_i 。
当 Z_i=1 时,将 X_i 与 Y_i 所在的集合合并。
当 Z_i=2 时,输出 X_i 与 Y_i 是否在同一集合内,是的输出
`Y` ;否则输出 `N` 。
分析
这就是一个简单并查集的实现,用数组来进行模拟,其中下标来表示节点,用数组中的值来表示根结点的位置
Python代码
class UnionFind:
def __init__(self, size):
# 初始化时,每个元素的父节点是它自己
self.parent = [i for i in range(size + 1)]
def find(self, x):
# 查找元素x所在的集合的代表(根节点),同时进行路径压缩
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
# 合并元素x和元素y所在的两个集合
rootX = self.find(x)
rootY = self.find(y)
if rootX != rootY:
self.parent[rootX] = rootY
def isConnected(self, x, y):
# 查询元素x和元素y是否在同一集合内
return self.find(x) == self.find(y)
def main():
N, M = map(int, input().split())
uf = UnionFind(N)
for _ in range(M):
Z, X, Y = map(int, input().split())
if Z == 1:
uf.union(X, Y)
elif Z == 2:
print('Y' if uf.isConnected(X, Y) else 'N')
if __name__ == "__main__":
main()
c
#include <stdio.h>
#include <stdlib.h>
// 并查集数组
int* parent;
// 查找元素x所在的集合的代表(根节点),同时进行路径压缩
int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
// 合并元素x和元素y所在的两个集合
void unionSets(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}
// 初始化并查集
void initUnionFind(int size) {
parent = (int*)malloc((size + 1) * sizeof(int));
for (int i = 0; i <= size; ++i) {
parent[i] = i;
}
}
int main() {
int N, M;
scanf("%d %d", &N, &M);
initUnionFind(N);
for (int i = 0; i < M; ++i) {
int Z, X, Y;
scanf("%d %d %d", &Z, &X, &Y);
if (Z == 1) {
unionSets(X, Y);
}
else if (Z == 2) {
if (find(X) == find(Y)) {
printf("Y\n");
}
else {
printf("N\n");
}
}
}
free(parent);
return 0;
}
今日分享题
洛谷P1387 最大正方形
P1387 最大正方形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
在一个 n* m 的只包含0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。
输入格式
输入文件第一行为两个整数 n,m,接下来 n 行,每行 m 个数字,用空格隔开,0 或 1。
输出格式
一个整数,最大正方形的边长。
分析:解决这道题的关键在于找到状态转移方程:dp[i][j]=MIN(MIN(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) +1
dp[i][j]
表示以 (i, j)
为右下角的最大正方形的边长
#include <stdio.h>
#include <stdlib.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
int maximalSquare(char matrix[][101], int n, int m) {
int dp[n][m];
int maxSide = 0;
// 初始化dp数组
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
dp[i][j] = 0;
}
}
// 动态规划填充dp数组
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (matrix[i][j] == '1') {
if (i == 0 || j == 0) { // 第一行或第一列
dp[i][j] = 1;
}
else {
dp[i][j] = MIN(MIN(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
}
if (dp[i][j] > maxSide) {
maxSide = dp[i][j];
}
}
}
}
return maxSide;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
char matrix[n][101]; // 假设m的最大值为100
for (int i = 0; i < n; i++) {
scanf("%s", matrix[i]);
}
printf("%d\n", maximalSquare(matrix, n, m));
return 0;
}