记录一下并查集的学习,参考网上答案,自己打上注释,学习用,
原题见浙大PAT--团体程序设计天梯赛-练习集
#include <iostream>
#include <string>
#include <cstdio>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <cmath>
#include <set>
#include <queue>
using namespace std;
#define MAX_NUM 10000
#define MAX_SON 5
/* 定义每个人的房产信息 */
typedef struct person
{
int id_self;
int id_father;
int id_mother;
int id_son[MAX_SON];
int homes;
int areas;
}Person;
/* 定义输出家族房产信息 */
typedef struct family
{
bool flag;
int id_min;
int peoples;
float avg_homes;
float avg_areas;
}Family;
// 保存输入的个人房产信息数组
Person in_put[MAX_NUM];
// 保存待输出的家族房产信息数组
Family out_put[MAX_NUM];
// 以个人id作为下标的访问标记数组
bool visit[MAX_NUM] = {false};
// 针对个人id操作的并查集数组
int union_find[MAX_NUM];
/*
并查集数组初始化
*/
void union_find_init(int *uf, int n)
{
for (int i = 0; i < n; ++i)
{
uf[i] = i;
}
}
/*
并查集递归找根
*/
int union_find_root(int *uf, int i)
{
if (uf[i] == i)
return i;
else
return union_find_root(uf, uf[i]);
}
/*
并查集合并为一个集合
*/
void union_find_merge(int *uf, int a, int b)
{
int root_a = union_find_root(uf, a);
int root_b = union_find_root(uf, b);
if (root_a == root_b) // 已是同一个集合
{
return;
} else // 对两个结点的根进行合并
{
if (root_a < root_b) // 不是同一个集合,合并为同一个集合,且较小id为根
{
uf[root_b] = root_a;
} else
{
uf[root_a] = root_b;
}
}
}
/*
自定义家族比较函数,用于sort排序
*/
bool comp_family(const Family &f1, const Family &f2)
{
if (f1.avg_areas == f2.avg_areas) // 平均面积相等
{
return (f1.id_min < f2.id_min); // 较小ID "较大"
} else
{
return (f1.avg_areas > f2.avg_areas);
}
}
int main()
{
int N;
cin >> N;
union_find_init(union_find, MAX_NUM); // 初始化并查集数组
// 接收输入,按照并查集操作合并家族成员
for (int i = 0; i < N; ++i)
{
cin >> in_put[i].id_self >> in_put[i].id_father >> in_put[i].id_mother;
visit[in_put[i].id_self] = true; // 标记此id有人员
if (in_put[i].id_father != -1) // 将父子合并为同一个家族
{
visit[in_put[i].id_father] = true;
union_find_merge(union_find, in_put[i].id_self, in_put[i].id_father);
}
if (in_put[i].id_mother != -1) // 将母子合并为同一个家族
{
visit[in_put[i].id_mother] = true;
union_find_merge(union_find, in_put[i].id_self, in_put[i].id_mother);
}
int K; // 孩子人数
cin >> K;
for (int j = 0; j < K; ++j)
{
cin >> in_put[i].id_son[j];
visit[in_put[i].id_son[j]] = true;
union_find_merge(union_find, in_put[i].id_self, in_put[i].id_son[j]);
}
cin >> in_put[i].homes >> in_put[i].areas;
}
// 对每个人的房产信息,找到其祖先,并记录在out_put[]中
for (int i = 0; i < N; ++i)
{
int id_root = union_find_root(union_find, in_put[i].id_self);
out_put[id_root].flag = true; // 标记为新家族
out_put[id_root].id_min = id_root; // 记录家族最小id
out_put[id_root].avg_homes += in_put[i].homes; // 累计家族家产
out_put[id_root].avg_areas += in_put[i].areas;
}
// 根据visit[]中记录的每个人id,统计处于同一个家族中的人数
// 同时根据out_put[]统计家族个数
int all_famaly(0);
for (int id = 0; id < MAX_NUM; ++id)
{
if (true == visit[id])
{
int id_root = union_find_root(union_find, id);
++(out_put[id_root].peoples); // 累计家族成员
}
if (true == out_put[id].flag)
{
++all_famaly; // 累计家族个数
}
}
// 计算各个家族的平均房产信息
for (int i = 0; i < MAX_NUM; ++i)
{
if (true == out_put[i].flag)
{
out_put[i].avg_homes = (1.0f * out_put[i].avg_homes) / (1.0f * out_put[i].peoples);
out_put[i].avg_areas = (1.0f * out_put[i].avg_areas) / (1.0f * out_put[i].peoples);
}
}
// 对各家族房产信息排序(由大到小)
sort(out_put, out_put+MAX_NUM, comp_family);
// 输出
cout << all_famaly << endl;
for (int i = 0; i < all_famaly; ++i) // 有效家族已排序到数组前端
{
printf("%.4d %d %.3f %.3f\n", out_put[i].id_min, out_put[i].peoples, \
out_put[i].avg_homes, out_put[i].avg_areas);
}
return 0;
}