7-11 家庭房产
给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。
输入格式:
输入第一行给出一个正整数N(≤1000),随后N行,每行按下列格式给出一个人的房产:
编号 父 母 k 孩子1 … 孩子k 房产套数 总面积
其中编号是每个人独有的一个4位数的编号;
父和母分别是该编号对应的这个人的父母的编号(如果已经过世,则显示-1);k(0≤k≤5)是该人的子女的个数;孩子i是其子女的编号。
输出格式:
首先在第一行输出家庭个数(所有有亲属关系的人都属于同一个家庭)。随后按下列格式输出每个家庭的信息:
家庭成员的最小编号 家庭人口数 人均房产套数 人均房产面积
其中人均值要求保留小数点后3位。家庭信息首先按人均面积降序输出,若有并列,则按成员编号的升序输出。
输入样例:
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
输出样例:
3
8888 1 1.000 1000.000
0001 15 0.600 100.000
5551 4 0.750 100.000
题解
这一题需要运用并查集的知识
并查集就是可以把有交集的集合关联在一起,是一种树形结构,每个有交集的集合都有共同的最上层父节点。
同时该数据结构需要定义两个操作find 和union/merge,分别代表寻找某节点的父节点和联合两个节点。
下面的注释对于下面应写的代码内容给了一个tips,可以先试着按部就班的写一写,然后出现问题再和后面的源代码比对。
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
//构造一个带有id,房子的数量、房子的面积、家庭人口数量的结构体
//结构体中需要有能返回人均房子面积,人均房子数量和重载比较运算符的函数(先按人均面积降序,并列按ID升序输出)
//创建一个这样的结构体数组,结构体数组的大小应该是由总人数决定的
//(这个并查集的最上层节点就是该群体里编号最小的元素,因为题目要求输出这个),然后整个集群的信息存储就放在最上层节点
//定义一个find函数,参数就是ID,find其实就是找上层节点的函数
//如果在结构体数组里面用索引访问,发现第x个元素的ID不是自己的话,就要递归find自己的上层ID,然后return最上层的值
//定义一个union函数,参数是两个ID(因为要联合嘛)
//先得到两个ID的最上层节点ID,进行判断,如果最上层一样,说明已经做过联合操作,直接返回即可
//如果x的最上层ID大于y的最上层ID,交换x和y,然后把x的最上层ID赋值给y的最上层的ID,也就是要确保小的ID在最上层
//然后把x的最上层ID的全部数据加上y的最上层的全部数据,最后把y的最上层的房屋数量标成-1,这样除了最上层的,其他的人均都小于0
//因为总共有1000条数据,每条数据最多有五个小孩,每个小孩最多有2个家长,所以创建一个大小为10001的标记数组
//因为总共有1000条数据,每条数据里面最多有1个ID 2个父母ID 1个小孩数量 5个小孩 ,房子那些是存在结构体的,所以这里开个
//[1001][10]的数组就好了。
int main()
{
//读入数据条数
//初始化结构体数组,所有的人的ID都是自己的索引,家庭人口数量定成1,房子的面积和数量定成0
//循环读入n组数据
//先把ID,父母编号和小孩数目读入
//分别把存在的人标记,然后父母编号不为-1要做一个判断
//循环读入这个人的小孩数据,然后打上标记
//读入这个人的家庭房产面积和数量,这个数据是存进结构体数组里面的
//循环n趟,把所有数据的自己和父母(需要判断是不是-1)做一个UNION操作,然后再内嵌一个循环UNION自己和自己的小孩
//对结构体数组进行排序(这也就是为什么要重载运算符,因为可以让数组以自己想要的方式排序。也可以重载cmp函数)
//创建一个计数的变量用来记录家庭数
//这个时候因为排序结束了,这时候在最前面的应该是各个集群的最上层节点,因为他们有子节点的家庭面积之和,其他的数据人均全是负,所以
//分布情况就是最上层、空白数据元素、非最上层元素,我们只需要获取最前面的就可以了。
//然后按格式输出.
}
源代码
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
//构造一个带有id,房子的数量、房子的面积、家庭人口数量的结构体
//结构体中需要有能返回人均房子面积,人均房子数量和重载比较运算符的函数(先按人均面积降序,并列按ID升序输出)
struct people
{
int ID;
int house_number, house_area, member_number;
double area_aver()const
{
return ((double)(house_area)) / ((double)(member_number));
}
double number_aver()const
{
return ((double)(house_number)) / ((double)(member_number));
}
bool operator<(const people &b)const
{
if (area_aver() != b.area_aver())
{
//cout << "ID:" << ID << " " << area_aver() << " " << " " << "b.ID:" << b.ID << " " << b.area_aver() << endl;
return area_aver() > b.area_aver();
}
else return ID < b.ID;
}
}PEOPLE[10001];
//创建一个这样的结构体数组,结构体数组的大小应该是由总人数决定的
//(这个并查集的最上层节点就是该群体里编号最小的元素,因为题目要求输出这个),然后整个集群的信息存储就放在最上层节点
//定义一个find函数,参数就是ID,find其实就是找上层节点的函数
//如果在结构体数组里面用索引访问,发现第x个元素的ID不是自己的话,就要递归find自己的上层ID,然后return最上层的值
int find(int x)
{
if (PEOPLE[x].ID != x)
PEOPLE[x].ID = find(PEOPLE[x].ID);
return PEOPLE[x].ID;
//return x == PEOPLE[x].ID ? x : find(PEOPLE[x].ID);
}
//定义一个union函数,参数是两个ID(因为要联合嘛)
//先得到两个ID的最上层节点ID,进行判断,如果最上层一样,说明已经做过联合操作,直接返回即可
//如果x的最上层ID大于y的最上层ID,交换x和y,然后把x的最上层ID赋值给y的最上层的ID,也就是要确保小的ID在最上层
//然后把x的最上层ID的全部数据加上y的最上层的全部数据,最后把y的最上层的房屋数量标成-1,这样除了最上层的,其他的人均都小于0
void _union(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)
return;
else if (x > y)
swap(x, y);
PEOPLE[y].ID = x;
PEOPLE[x].house_area += PEOPLE[y].house_area;
PEOPLE[x].house_number += PEOPLE[y].house_number;
PEOPLE[x].member_number += PEOPLE[y].member_number;
PEOPLE[y].member_number = -1;
PEOPLE[y].house_area = PEOPLE[x].house_area;
}
//因为总共有1000条数据,每条数据最多有五个小孩,每个小孩最多有2个家长,所以创建一个大小为10001的标记数组
bool visit[10001];
//因为总共有1000条数据,每条数据里面最多有1个ID 2个父母ID 1个小孩数量 5个小孩 ,房子那些是存在结构体的,所以这里开个
//[1001][10]的数组就好了。
int temp[1005][10];
int main()
{
//ios::sync_with_stdio(false);
//cout.tie(NULL);
//cin.tie(NULL);
//读入数据条数
int n;
cin >> n;
//初始化结构体数组,所有的人的ID都是自己的索引,家庭人口数量定成1,房子的面积和数量定成0
for (int i = 0; i < 10001; ++i)
{
PEOPLE[i].ID = i;
PEOPLE[i].house_area = 0;
PEOPLE[i].house_number = 0;
PEOPLE[i].member_number = 1;
}
//循环读入n组数据
for (int i = 0; i < n; ++i)
{
//先把ID,父母编号和小孩数目读入
cin >> temp[i][1] >> temp[i][2] >> temp[i][3] >> temp[i][4];
//分别把存在的人标记,然后父母编号不为-1要做一个判断
visit[temp[i][1]] = true;
if (temp[i][2] != -1)
visit[temp[i][2]] = true;
if (temp[i][3] != -1)
visit[temp[i][3]] = true;
//循环读入这个人的小孩数据,然后打上标记
for (int j = 0; j < temp[i][4]; ++j)
{
cin >> temp[i][5 + j];
visit[temp[i][5 + j]] = true;
}
//读入这个人的家庭房产面积和数量,这个数据是存进结构体数组里面的
cin >> PEOPLE[temp[i][1]].house_number >> PEOPLE[temp[i][1]].house_area;
}
//循环n趟,把所有数据的自己和父母(需要判断是不是-1)做一个UNION操作,然后再内嵌一个循环UNION自己和自己的小孩
for (int i = 0; i < n; ++i)
{
if (temp[i][2] != -1)
_union(temp[i][1], temp[i][2]);
if (temp[i][3] != -1)
_union(temp[i][1], temp[i][3]);
for (int j = 0; j < temp[i][4]; ++j)
{
_union(temp[i][1], temp[i][j + 5]);
}
}
//对结构体数组进行排序(这也就是为什么要重载运算符,因为可以让数组以自己想要的方式排序。也可以重载cmp函数)
sort(PEOPLE, PEOPLE + 10000);
//创建一个计数的变量用来记录家庭数
int cnt = 0;
//这个时候因为排序结束了,这时候在最前面的应该是各个集群的最上层节点,因为他们有子节点的家庭面积之和,其他的数据人均全是负,所以
//分布情况就是最上层、空白数据元素、非最上层元素,我们只需要获取最前面的就可以了。
while (visit[PEOPLE[cnt].ID])
cnt++;
//然后按格式输出.
cout << cnt << endl;
for (int i = 0; i < cnt; ++i)
{
printf("%04d %d ", PEOPLE[i].ID, PEOPLE[i].member_number);
printf("%.3f %.3f\n", PEOPLE[i].number_aver(), PEOPLE[i].area_aver());
}
//if (PEOPLE[1] < PEOPLE[10])
// cout << "true";
//return 0;
}
/*
1
0000 9999 0001 5 0002 0003 0004 0005 0006 1 100
*/
本文介绍如何使用并查集算法解决家庭成员与房产统计问题。通过输入家庭成员关系和房产信息,算法能够统计每个家庭的人口数、人均房产面积及房产套数。文章详细解释了并查集的原理,包括find和union操作,以及如何利用这些操作来关联有亲属关系的人和房产数据。
&spm=1001.2101.3001.5002&articleId=104196251&d=1&t=3&u=713144d8a4694c38b8b909f4352fdfab)
859

被折叠的 条评论
为什么被折叠?



