L2-007 家庭房产

记录一下并查集的学习,参考网上答案,自己打上注释,学习用,

原题见浙大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;

}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值