PAT ~ L2-007. 家庭房产 (并查集)

思路:一看什么几个家庭什么的,就想到了用并查集去维护家庭关系。输入的处理有点麻烦,题目中说了每个人的编号是一个不重复的四位数字,所以我们可以借鉴 PAT ~ L2-002. 链表去重 (思路 + 模拟)这道题目,直接把数组下标就作为是哪个人。

每个人需要记录它属于哪个家庭(即编号最小的人的编号),家庭总人数,家庭总房产套数,家庭总房产面积。因为人均过程中有精度损失,所以先保存总的。合并过程中注意要合并到编号小的上面,且家庭总人数,家庭总房产套数,家庭总房产面积也都要进行累加。

初始化时使家庭总人数=0,如果关系涉及到该人时,他的家庭总人数为0,那么就变为1,然后在进行合并。

最终,自己的编号==自己的父节点的,且家庭人数不为0,那么就是一个家庭。然后把这个信息copy到另一个ans数组里,

对ans数组按要求排序输出即可。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10005;
int n;
struct Node
{
    int id, cnt;//家庭成员的最小编号 家庭人口数
    double num, area;//总房产套数 总房产面积
}a[MAXN], ans[MAXN];
void init()
{
    for (int i = 0; i < MAXN; i++)
    {
        a[i].id = i; a[i].cnt = 0;
        a[i].num = 0.0; a[i].area = 0.0;
    }
}
int Find(int x)
{
    if (a[x].id == x) return x;
    a[x].id = Find(a[x].id);
    return a[x].id;
}
void Union(int x, int y)
{
    int root1 = min(Find(x), Find(y)), root2 = max(Find(x), Find(y));
    //注意编号小的做根
    if (root1 != root2)
    {
        a[root1].cnt += a[root2].cnt;
        a[root1].num += a[root2].num;
        a[root1].area += a[root2].area;
        a[root2].id = root1;
    }
}
bool cmp(Node a, Node b)
{
    if (a.area / a.cnt != b.area / b.cnt)
        return a.area / (double)a.cnt > b.area / (double)b.cnt;
    return a.id < b.id;
}
int main()
{
    init();
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
    {
        int id, father, mather, k, child, num, area;
        scanf("%d%d%d%d", &id, &father, &mather, &k);
        if (a[id].cnt == 0) a[id].cnt = 1;
        if (father != -1)
        {
            if (a[father].cnt == 0) a[father].cnt = 1;
            Union(id, father);
        }
        if (mather != -1)
        {
            if (a[mather].cnt == 0) a[mather].cnt = 1;
            Union(id, mather);
        }
        while (k--)
        {
            scanf("%d", &child);
            if (a[child].cnt == 0) a[child].cnt = 1;
            Union(id, child);
        }
        scanf("%d%d", &num, &area);
        a[Find(id)].num += num;
        a[Find(id)].area += area;
    }
    int cnt = 0;//家庭数
    for (int i = 0; i < MAXN; i++)
    {
        if (a[i].id == i && a[i].cnt != 0)
        {
            ans[cnt++] = a[i];
        }
    }
    sort(ans, ans + cnt, cmp);
    printf("%d\n", cnt);
    for (int i = 0; i < cnt; i++)
    {
        printf("%04d %d ", ans[i].id, ans[i].cnt);
        printf("%.3f %.3f\n", ans[i].num / (double)ans[i].cnt, ans[i].area / (double)ans[i].cnt);
    }
    return 0;
}
/*
10
6666 5551 5552 1 7777 1 100
1234 5678 9012 1 0002 2 300
8888 -1 -1 0 1 1000
2468 0001 0004 1 2222 1 500
7777 6666 -1 0 2 300
3721 -1 -1 1 2333 2 150
9012 -1 -1 3 1236 1235 1234 1 100
1235 5678 9012 0 1 50
2222 1236 2468 2 6661 6662 1 300
2333 -1 3721 3 6661 6662 6663 1 100
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值