PAT甲级——1114 Family Property (并查集)

博客围绕家庭房产数据分类问题展开,根据给定的人员家庭信息和房产信息,需计算各家庭的规模、平均房产数量和面积。采用并查集对家庭进行分类,对并查集操作稍作修改以方便找出最小成员ID,同时用visit数组标记已访问ID防止重复计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1114 Family Property (25 分)

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 Child​1​​⋯Child​k​​ M​estate​​ 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 (0≤k≤5) is the number of children of this person; Child​i​​'s are the ID's of his/her children; M​estate​​ 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 AVG​sets​​ AVG​area​​

where ID is the smallest ID in the family; M is the total number of family members; AVG​sets​​ is the average number of sets of their real estate; and AVG​area​​ 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

 题目大意:根据给出的数据分类出各个家庭,然后输出家庭个数;接下来输出每个家庭的成员ID(只需输出家庭成员中最小的ID)、成员数量、平均房产数量、平均房产面积,输出顺序为平均房产面积降序、ID升序。

思路:用并查集S给家庭分类,为了方便找出最小的家庭成员ID,对并查集的操作稍作修改,集合元素初始化为对应的下标,若S[X]==X,则X为根,unionSet()函数将大根集合并到小根集合里;家庭成员计数时需要对已经访问过的ID进行标记,防止重复计数,用visit数组标记每个ID。

总结:其实我是比较害怕这类问题的,看起来简单,可是信息一多思考起来就容易混乱,需要很细心很耐心地分析,用不到什么算法知识却超级消耗时间~

#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
struct node1 {
	int ID, fID, mID, k, Me, area;
	vector <int> child;
};
struct node2 {
	int sumM = 0, sumSets = 0, sumArea = 0;
};
struct node3 {
	int ID, M;
	float AVGs, AVGa;
};
vector <int> S(10000);
vector <bool> visit(10000, false);
void initialize();//对集合S进行初始化
bool cmp(node3 &a, node3 &b);//排序规则
int getNum(node1 &a);//获取家庭成员的数量
int find(int X);
void unionSet(int X, int Y);
int main()
{
	int N;
	scanf("%d", &N);
	vector <node1> data(N);
	unordered_map <int, node2> mp;
	initialize();
	for (int i = 0; i < N; i++) {
		node1 tmp;
		scanf("%d%d%d%d", &tmp.ID, &tmp.fID, &tmp.mID, &tmp.k);
		if (tmp.fID != -1)
			unionSet(tmp.ID, tmp.fID);
		if (tmp.mID != -1)
			unionSet(tmp.ID, tmp.mID);
		if (tmp.k != 0) {
			for (int j = 0; j < tmp.k; j++) {
				int cID;
				scanf("%d", &cID);
				tmp.child.push_back(cID);
				unionSet(tmp.ID, cID);
			}
		}
		scanf("%d%d", &tmp.Me, &tmp.area);
		data[i] = tmp;
	}
	for (int i = 0; i < N; i++) {
		int ID = find(data[i].ID);
		mp[ID].sumM += getNum(data[i]);
		mp[ID].sumSets += data[i].Me;
		mp[ID].sumArea += data[i].area;
	}
	vector <node3> ans;
	for (auto it = mp.begin(); it != mp.end(); it++) {
		int ID = it->first,
			M = it->second.sumM;
		float AVGs = it->second.sumSets*1.0 / M,
			AVGa = it->second.sumArea*1.0 / M;
		ans.push_back({ ID,M,AVGs,AVGa });
	}
	sort(ans.begin(), ans.end(), cmp);
	printf("%d\n", ans.size());
	for (int i = 0; i < ans.size(); i++)
		printf("%04d %d %.3f %.3f\n", ans[i].ID, ans[i].M, ans[i].AVGs, ans[i].AVGa);
	return 0;
}
int getNum(node1 &a) {
	int n = 0;
	if (!visit[a.ID]) {
		n++;
		visit[a.ID] = true;
	}
	if ((a.fID != -1 )&& (!visit[a.fID])) {
		n++;
		visit[a.fID] = true;
	}
	if ((a.mID != -1) && (!visit[a.mID])) {
		n++;
		visit[a.mID] = true;
	}
	if (a.k != 0) {
		for (int i = 0; i < a.k; i++) {
			if (!visit[a.child[i]]) {
				n++;
				visit[a.child[i]] = true;
			}
		}
	}
	return n;
}
void unionSet(int X, int Y) {
	int rootX = find(X),
		rootY = find(Y);
	if (rootX > rootY)
		S[rootX] = S[rootY];
	else 
		S[rootY] = S[rootX];
}
int find(int X) {
	if (S[X] == X) {
		return X;
	}
	else {
		return S[X] = find(S[X]);//递归地压缩路径
	}
}
void initialize() {
	for (int i = 0; i < 10000; i++) 
		S[i] = i;//将集合元素初始化为对应的下标,每个集合的根节点就是当前家庭的最小ID
}
bool cmp(node3 &a, node3 &b) {
	if (a.AVGa != b.AVGa) 
		return a.AVGa > b.AVGa;
	else 
		return a.ID < b.ID;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值