#1114. Family Property【并查集】

原题链接

Problem Description:

This time, you are supposed to help us collect the data for family-owned property. Given each person’s family members, and the estate(房产)info under his/her own name, we need to know the size of each family, and the average area and number of sets of their real estate.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤1000). Then N lines follow, each gives the infomation of a person who owns estate in the format:

ID Father Mother k k k C h i l d 1 Child_1 Child1 ⋯ \cdots C h i l d k Child_k Childk M e s t a t e M_{estate} Mestate Area

where ID is a unique 4-digit identification number for each person; Father and Mother are the ID's of this person’s parents (if a parent has passed away, -1 will be given instead); k k k ( 0 ≤ k ≤ 5 0\leq k\leq 5 0k5) is the number of children of this person; C h i l d i Child_i Childi's are the ID's of his/her children; M e s t a t e M_{estate} Mestate is the total number of sets of the real estate under his/her name; and Area is the total area of his/her estate.

Output Specification:

For each case, first print in a line the number of families (all the people that are related directly or indirectly are considered in the same family). Then output the family info in the format:

ID M A V G s e t s AVG_{sets} AVGsets A V G a r e a AVG_{area} AVGarea

where ID is the smallest ID in the family; M is the total number of family members; KaTeX parse error: Expected 'EOF', got '}' at position 10: AVG_[sets}̲ is the average number of sets of their real estate; and A V G a r e a AVG_{area} AVGarea is the average area. The average numbers must be accurate up to 3 decimal places. The families must be given in descending order of their average areas, and in ascending order of the ID’s if there is a tie.

Sample Input:

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

Sample Output:

3
8888 1 1.000 1000.000
0001 15 0.600 100.000
5551 4 0.750 100.000

Problem Analysis:

大概思路就是,将所有有血缘关系的人,即一个家庭的所有人统统维护至一个集合,除了用并查集维护这样的连通性外,我们还需要维护一些其他的集合额外信息,例如这个集合的最小编号、这个集合的人均房产总数、人均房产面积等。

对于集合最小编号,我们不妨每次合并集合时都将编号较大的那个合并至编号较小的那个 id 上,这样最终的集合根节点就是整个集合编号最小的 id,这样做的好处就是不用另外维护最小编号了。

由于本题涉及到的关系较多,一个 id 最多可以有七条关系,因此我们可以事先将所有的边关系用一结构体存起来,然后离线处理:

struct Edge
{
	int a, b;
}e[N];

另外,由于本题的编号不是连续的一段数,而是分布在 0 ∼ 9999 0\sim 9999 09999 中的任意数,并且总数不超过 1000 1000 1000,因此在读入时我们还需要一个标记数组 st[] 来标记出现过的 id,在遍历时只需要遍历范围内被标记过的 id 即可。

至于题目中要求我们按照双关键字(人均房产面积和 id 大小)来进行排序输出,我们则可以用一结构体存每个家庭,然后用 vector<Familiy> 来存所有集合,调用 sort 函数进行排序输出。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int N = 10010;

int n, m;
int p[N], c[N], hc[N], ha[N];  
// 并查集数组,每个集合人数,房子数量,房子面积
bool st[N];

struct Edge
{
    int a, b;
}e[N];  // 每个人最多有父母,5个孩子。即7条边,一共1000个人,总共7000条边

struct Family
{
    int id, c, hc, ha;
    bool operator< (const Family &t) const
    {
        //  ha / c, t.ha / t.c,用分数相乘方法进行比较
        // ha/c - t.ha/t.c = (ha * t.c - t.ha * c) / c * t.c
        if (ha * t.c != t.ha * c) return ha * t.c > t.ha * c;
        return id < t.id;
    }
};

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )  // 处理读入
    {
        int id, father, mother, k;
        cin >> id >> father >> mother >> k;

        st[id] = true;
        if (father != -1) e[m ++ ] = {id, father};
        if (mother != -1) e[m ++ ] = {id, mother};

        for (int j = 0; j < k; j ++ )
        {
            int son;
            cin >> son;
            e[m ++ ] = {id, son};
        }
        cin >> hc[id] >> ha[id];
    }

    for (int i = 0; i < N; i ++ ) p[i] = i, c[i] = 1;  // 初始化并查集
    for (int i = 0; i < m; i ++ )  // 通过存储的边的信息来处理并查集连通性
    {
        int a = e[i].a, b = e[i].b;
        st[a] = st[b] = true;

        int pa = find(a), pb = find(b);
        if (pa != pb)
        {
            if (pb > pa) swap(pa, pb);  // 为了方便,每次让pb那个成为最小的
            c[pb] += c[pa];
            hc[pb] += hc[pa];
            ha[pb] += ha[pa];
            p[pa] = pb;
        }
    }

    vector<Family> family;
    for (int i = 0; i < N; i ++ )
        if (st[i] && p[i] == i)  // 这是一个家庭代表
            family.push_back({i, c[i], hc[i], ha[i]});
        
    sort(family.begin(), family.end());

    cout << family.size() << endl;

    for (int i = 0; i < family.size(); i ++ )
        printf("%04d %d %.3f %.3f\n", family[i].id, family[i].c, 
                                    family[i].hc * 1.0 / family[i].c, 
                                    family[i].ha * 1.0 / family[i].c);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值