数据结构与算法A实验六图论---7-6 家庭房产(并查集)

给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。

输入格式:

输入第一行给出一个正整数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

结尾无空行

思路:算法部分就是简单的并查集,但数据处理等方面还挺复杂,一定要仔细。首先用个data结构体数组来存储输入的每个人的信息,用node结构体来存储每个家族的信息,其中包括该家族最小成员编号,人数,人均房产数量和人均房产面积,其中flag用来标识家族是否存在,因为数据量不大,要注意编码细节(人数1005和家庭数10005的设置)。输入人的信息时,将他和其父母孩子合并,并使祖先的编号最小,用vis数组标记该编号是否出现。合并之后遍历n个人,将其房产信息都加入到家族结构体中,最后还要排序,先按人均房产面积降序排列,再按编号升序排列。

#include<bits/stdc++.h>
using namespace std;

struct data //存储输入的每个人的信息
{
	int id, fa, ma, k, num, area;
	int ch[6];
}a[1005];

struct node //存储每个家族的信息,flag用来标识是否存在
{
	int idm, pp, flag;
	double num, area;
}fam[10005];

int root[10005], vis[10005], n, res = 0; //res家庭数量

int getr(int x) {
	if (root[x] == x)
		return x;
	else
		return root[x] = getr(root[x]);
}

void Union(int x, int y) {
	int xx = getr(x);
	int yy = getr(y);
	if (xx < yy)
		root[yy] = xx;
	else
		root[xx] = yy;
}

bool cmp(node x, node y) {
	if (x.area == y.area)
		return x.idm < y.idm;
	else
		return x.area > y.area;
}

int main() {
	cin >> n;
	int i, j;
	for (i = 0; i < 10000; i++) {
		root[i] = i;
	}
	for (i = 0; i < n; i++) {
		cin >> a[i].id >> a[i].fa >> a[i].ma >> a[i].k;
		for (j = 0; j < a[i].k; j++)
			cin >> a[i].ch[j];
		cin >> a[i].num >> a[i].area;
		vis[a[i].id] = 1;
		if (a[i].fa != -1) {
			vis[a[i].fa] = 1;
			Union(a[i].id, a[i].fa);
		}
		if (a[i].ma != -1) {
			vis[a[i].ma] = 1;
			Union(a[i].id, a[i].ma);
		}
		for (j = 0; j < a[i].k; j++) {
			vis[a[i].ch[j]] = 1;
			Union(a[i].id, a[i].ch[j]);
		}
	}
	for (i = 0; i < n; i++) {
		int idr = getr(a[i].id);
		fam[idr].idm = idr;
		fam[idr].num += a[i].num;
		fam[idr].area += a[i].area;
		fam[idr].flag = 1;
	}
	for (i = 0; i < 10000; i++) {
		if (vis[i])
			fam[getr(i)].pp++;
		if (fam[i].flag)
			res++;
	}
	for (i = 0; i < 10000; i++) {
		if (fam[i].flag) {
			fam[i].num = fam[i].num * 1.0 / fam[i].pp;
			fam[i].area = fam[i].area * 1.0 / fam[i].pp;
		}
	}
	sort(fam, fam + 10000, cmp);
	cout << res << endl;
	for (i = 0; i < res; i++)
		printf("%04d %d %.3f %.3f\n", fam[i].idm, fam[i].pp, fam[i].num, fam[i].area); //注意编号的输出
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

趟水过河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值