解题思路
这是一道并查集问题,主要思路如下:
-
数据结构:
- 使用并查集维护集合关系
- 使用
map
对数字进行离散化处理 - 使用
vector
存储每个集合的元素
-
处理步骤:
- 读入每个集合的元素并进行离散化
- 对每个集合内的元素进行合并
- 统计最终的集合数量和最大集合大小
-
关键点:
- 使用路径压缩优化并查集
- 维护每个集合的元素数量
- 处理重复元素
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
int pre[MAXN], count[MAXN];
int mark[MAXN];
vector<int> sets[MAXN];
map<int, int> mp;
// 初始化并查集
void init() {
for(int i = 1; i < MAXN; i++) {
pre[i] = i;
count[i] = 1;
}
}
// 查找父节点(路径压缩)
int find(int x) {
if(x == pre[x]) return x;
return (pre[x] = find(pre[x]));
}
// 读取一行数据
void readLine(vector<int>& vec) {
char ch;
int num = 0;
while((ch = getchar()) != '\n') {
if(ch != ' ') {
num = num * 10 + (ch - '0');
} else {
vec.push_back(num);
num = 0;
}
}
vec.push_back(num);
}
int main() {
init();
int N, ret = 0, maxSize = 0;
scanf("%d", &N);
getchar();
// 读入数据
for(int i = 1; i <= N; i++) {
readLine(sets[i]);
}
// 离散化处理
int num = 0;
for(int i = 1; i <= N; i++) {
for(int& x: sets[i]) {
if(mp.count(x) == 0) {
mp[x] = ++num;
}
x = mp[x];
}
}
// 合并集合
for(int i = 1; i <= N; i++) {
int root = find(sets[i][0]);
for(int j = 1; j < sets[i].size(); j++) {
int curr = find(sets[i][j]);
if(root != curr) {
pre[curr] = root;
count[root] += count[curr];
}
}
}
// 统计结果
for(int i = 1; i <= num; i++) {
int root = find(i);
if(!mark[root]) {
mark[root] = 1;
ret++;
maxSize = max(maxSize, count[root]);
}
}
cout << ret << endl << maxSize << endl;
return 0;
}
import java.util.*;
public class Main {
static final int MAXN = 500010;
static int[] parent = new int[MAXN];
static int[] count = new int[MAXN];
static boolean[] mark = new boolean[MAXN];
// 初始化并查集
static void init() {
for(int i = 1; i < MAXN; i++) {
parent[i] = i;
count[i] = 1;
}
}
// 查找父节点
static int find(int x) {
if(x == parent[x]) return x;
return parent[x] = find(parent[x]);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
init();
int N = sc.nextInt();
sc.nextLine();
List<List<Integer>> sets = new ArrayList<>();
Map<Integer, Integer> map = new HashMap<>();
// 读入数据并离散化
int num = 0;
for(int i = 0; i < N; i++) {
String[] line = sc.nextLine().split(" ");
List<Integer> set = new ArrayList<>();
for(String s : line) {
int val = Integer.parseInt(s);
if(!map.containsKey(val)) {
map.put(val, ++num);
}
set.add(map.get(val));
}
sets.add(set);
}
// 合并集合
for(List<Integer> set : sets) {
int root = find(set.get(0));
for(int i = 1; i < set.size(); i++) {
int curr = find(set.get(i));
if(root != curr) {
parent[curr] = root;
count[root] += count[curr];
}
}
}
// 统计结果
int result = 0, maxSize = 0;
for(int i = 1; i <= num; i++) {
int root = find(i);
if(!mark[root]) {
mark[root] = true;
result++;
maxSize = Math.max(maxSize, count[root]);
}
}
System.out.println(result);
System.out.println(maxSize);
}
}
import sys
sys.setrecursionlimit(1000000)
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.count = [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):
px, py = self.find(x), self.find(y)
if px != py:
self.parent[py] = px
self.count[px] += self.count[py]
# 读取输入
N = int(input())
sets = []
for _ in range(N):
sets.append(list(map(int, input().split())))
# 离散化处理
num_map = {}
curr_num = 0
for set_i in sets:
for i, num in enumerate(set_i):
if num not in num_map:
num_map[num] = curr_num
curr_num += 1
set_i[i] = num_map[num]
# 初始化并查集
uf = UnionFind(curr_num)
# 合并集合
for set_i in sets:
root = uf.find(set_i[0])
for i in range(1, len(set_i)):
uf.union(root, set_i[i])
# 统计结果
mark = set()
result = 0
max_size = 0
for i in range(curr_num):
root = uf.find(i)
if root not in mark:
mark.add(root)
result += 1
max_size = max(max_size, uf.count[root])
print(result)
print(max_size)
算法及复杂度
- 算法:并查集 + 离散化
- 时间复杂度:
-
为元素总数,
为阿克曼函数的反函数
- 空间复杂度:
- 需要存储并查集和集合信息