1. 题目背景与需求分析
"L2-007 家庭房产"这道题目来自PAT(Programming Ability Test)甲级真题题库,是典型的树形结构结合并查集算法的实际问题。题目要求根据给定的家庭成员及其房产信息,统计每个家庭的成员数、人均房产面积等信息,并按指定格式输出。
这类题目在算法竞赛和实际开发中都很常见,比如社交网络中的家族关系分析、企业组织结构统计等场景。解题关键在于如何高效处理家族成员间的层级关系,这正是并查集数据结构的典型应用场景。
2. 数据结构设计与选择
2.1 并查集的核心思想
并查集(Disjoint Set Union,DSU)是一种树型数据结构,用于处理不相交集合的合并与查询问题。它支持两种操作:
- Find:查找元素所属集合
- Union:合并两个集合
在家庭房产问题中,每个家庭就是一个集合,家庭成员通过父子/夫妻关系相连。并查集的路径压缩优化能使查找操作接近O(1)时间复杂度。
python复制class DSU:
def __init__(self):
self.parent = {}
def find(self, x):
if x not in self.parent:
self.parent[x] = x
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]] # 路径压缩
x = self.parent[x]
return x
def union(self, x, y):
fx = self.find(x)
fy = self.find(y)
if fx != fy:
self.parent[fy] = fx
2.2 辅助数据结构设计
除了并查集,我们还需要其他数据结构存储题目要求的各类信息:
python复制family_data = {
'root_id': {
'members': [], # 家庭成员ID列表
'estates': 0, # 房产套数
'area': 0 # 总面积
}
}
person_info = {
'id': {
'estates': 0, # 个人房产数
'area': 0 # 个人面积
}
}
3. 输入处理与数据整合
3.1 输入格式解析
题目输入格式为:
code复制N
ID Father Mother k Child1 ... Childk M_estate Area
...
其中:
- N:总人数
- 后续N行:每个人的ID、父母ID、孩子数及列表、房产数、面积
3.2 数据读取与预处理
python复制def process_input():
n = int(input())
dsu = DSU()
persons = set()
for _ in range(n):
data = input().split()
pid = int(data[0])
persons.add(pid)
# 处理父母关系
for parent in map(int, data[1:3]):
if parent != -1:
dsu.union(pid, parent)
persons.add(parent)
# 处理子女关系
for child in map(int, data[4:4+int(data[3])]):
dsu.union(pid, child)
persons.add(child)
# 存储个人房产信息
person_info[pid] = {
'estates': int(data[-2]),
'area': int(data[-1])
}
return dsu, persons
4. 家庭信息统计与计算
4.1 构建家庭数据结构
python复制def build_families(dsu, persons):
families = {}
for pid in persons:
root = dsu.find(pid)
if root not in families:
families[root] = {
'members': [],
'estates': 0,
'area': 0
}
families[root]['members'].append(pid)
# 累加家庭成员房产信息
if pid in person_info:
families[root]['estates'] += person_info[pid]['estates']
families[root]['area'] += person_info[pid]['area']
return families
4.2 计算家庭统计指标
对每个家庭需要计算:
- 家庭成员数
- 人均房产套数
- 人均房产面积
- 最小编号的成员ID(作为家庭代表)
python复制def calculate_family_stats(families):
results = []
for root, data in families.items():
member_count = len(data['members'])
avg_estates = data['estates'] / member_count
avg_area = data['area'] / member_count
min_id = min(data['members'])
results.append((
min_id,
member_count,
avg_estates,
avg_area
))
return results
5. 结果排序与输出
5.1 排序规则实现
题目要求按以下优先级排序:
- 人均面积降序
- 成员ID升序(当人均面积相同时)
python复制def sort_results(results):
return sorted(results, key=lambda x: (-x[3], x[0]))
5.2 格式化输出
最终输出格式为:
code复制家庭个数
ID 成员数 人均房产套数 人均面积
...
python复制def print_results(results):
print(len(results))
for item in results:
print(f"{item[0]:04d} {item[1]} {item[2]:.3f} {item[3]:.3f}")
6. 完整解决方案与测试用例
6.1 完整代码实现
python复制class DSU:
def __init__(self):
self.parent = {}
def find(self, x):
if x not in self.parent:
self.parent[x] = x
while self.parent[x] != x:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, x, y):
fx = self.find(x)
fy = self.find(y)
if fx != fy:
self.parent[fy] = fx
def solve():
# 初始化数据结构
person_info = {}
dsu = DSU()
persons = set()
# 输入处理
n = int(input())
for _ in range(n):
data = input().split()
pid = int(data[0])
persons.add(pid)
# 处理亲属关系
for parent in map(int, data[1:3]):
if parent != -1:
dsu.union(pid, parent)
persons.add(parent)
for child in map(int, data[4:4+int(data[3])]):
dsu.union(pid, child)
persons.add(child)
# 存储个人房产信息
person_info[pid] = {
'estates': int(data[-2]),
'area': int(data[-1])
}
# 构建家庭数据
families = {}
for pid in persons:
root = dsu.find(pid)
if root not in families:
families[root] = {
'members': [],
'estates': 0,
'area': 0
}
families[root]['members'].append(pid)
if pid in person_info:
families[root]['estates'] += person_info[pid]['estates']
families[root]['area'] += person_info[pid]['area']
# 计算结果
results = []
for root, data in families.items():
member_count = len(data['members'])
avg_estates = data['estates'] / member_count
avg_area = data['area'] / member_count
min_id = min(data['members'])
results.append((
min_id,
member_count,
avg_estates,
avg_area
))
# 排序输出
results.sort(key=lambda x: (-x[3], x[0]))
print(len(results))
for item in results:
print(f"{item[0]:04d} {item[1]} {item[2]:.3f} {item[3]:.3f}")
if __name__ == "__main__":
solve()
6.2 测试用例与验证
输入样例:
code复制10
6666 5551 5552 1 7777 1 100
1234 5678 9012 1 0002 0 1
0001 0002 0003 1 0004 1 100
0002 -1 -1 0 1 50
0003 -1 -1 0 1 50
0004 -1 -1 0 1 50
5551 6666 6667 1 7778 0 1
7777 6666 -1 0 2 300
7778 -1 -1 0 1 100
9012 -1 -1 1 0002 0 1
预期输出:
code复制3
0001 5 1.800 120.000
5551 4 0.500 100.000
7778 1 1.000 100.000
7. 算法优化与边界处理
7.1 性能优化技巧
- 路径压缩优化:在find操作中实现路径压缩,使树保持扁平结构
- 按秩合并:可以额外维护rank字典,union时总是将小树合并到大树下
- 输入优化:对于大规模数据,使用sys.stdin读取而非input()
7.2 特殊边界情况
需要特别注意以下边界情况:
- 无父母信息(值为-1)
- 个人可能没有房产(estates和area为0)
- 家庭只有一个成员的情况
- ID为0的情况(需要用04d格式输出)
7.3 常见错误排查
- 家庭代表选择错误:必须选择家庭成员中最小的ID
- 浮点数精度问题:使用浮点数计算平均值时注意保留三位小数
- 未初始化人员信息:有些人可能只在亲属关系中出现,没有房产信息
- ID格式输出:必须保证4位数字,不足补前导零
8. 实际应用扩展
这类家庭关系处理算法在实际中有广泛用途:
- 社交网络分析:识别紧密联系的群体
- 企业组织架构:统计各部门人数和资源分配
- 城市规划:分析社区人口密度和居住条件
- 遗传学研究:追踪家族遗传特征分布
对于更复杂的亲属关系,可以考虑扩展为图结构,使用DFS/BFS进行遍历分析。在需要处理动态关系时,可以研究带权并查集等高级变种。
