给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。
输入格式:
输入第一行给出一个正整数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
分析
看到要求输出每个家庭的信息,很容易想到用并查集来把输入的这些人分类归为哪个家庭。那么问题来了,如何知道每个家庭成员的最小编号,以及人数,人均房数,人均面积?这个时候就需要使用辅助数组了。
void join(int a,int b){
ex[a]=true;
ex[b]=true;
a=find(a);
b=find(b);
if(a!=b){
int mx=max(a,b),mn=min(a,b);
re[mx]=mn;//以编号小的为根
pe[mn]+=pe[mx];//添加到编号小的上
ho[mn]+=ho[mx];
area[mn]+=area[mx];
}
}
struct family{//一个家庭
int id,pp,hnum,anum;//最小成员id,家庭人数,总房数,总面积
bool operator<(const family &a)const{
double avga1,avga2;
avga1=anum*1.0/pp;
avga2=a.anum*1.0/a.pp;
if(avga1!=avga2)return avga1>avga2;
return id<a.id;
}
};
for(int i=0;i<N;i++){
if(ex[i]&&re[i]==i){//如果此人真实存在且为根
families.push_back({i,pe[i],ho[i],area[i]});
}
}
像这样,用ex数组来标记存在的人,以编号小的那个人为根,将人数,房数,面积,全都累加到那一个人身上。用结构体数组来存储家庭信息,然后遍历并查集,将信息录入结构体然后排序输出结果
答案
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,cnt=0,k;
struct edge{//关系边
int a;
int b;
}ed[N];
struct family{//一个家庭
int id,pp,hnum,anum;//最小成员id,家庭人数,总房数,总面积
bool operator<(const family &a)const{
double avga1,avga2;
avga1=anum*1.0/pp;
avga2=a.anum*1.0/a.pp;
if(avga1!=avga2)return avga1>avga2;
return id<a.id;
}
};
int re[N],pe[N],ho[N],area[N];//并查集容器、记录人数,记录房数,记录面积
bool ex[N]={false};//判断并查集中哪些人真实存在
void init(){//初始化
for(int i=0;i<N;i++){
re[i]=i;
pe[i]=1;
}
}
int find(int a){
if(re[a]==a)return a;
return re[a]=find(re[a]);
}
void join(int a,int b){
ex[a]=true;
ex[b]=true;
a=find(a);
b=find(b);
if(a!=b){
int mx=max(a,b),mn=min(a,b);
re[mx]=mn;//以编号小的为根
pe[mn]+=pe[mx];//添加到编号小的上
ho[mn]+=ho[mx];
area[mn]+=area[mx];
}
}
int main() {
cin>>n;
init();
int id,fa,mo,child;
for(int i=0;i<n;i++){
cin>>id>>fa>>mo>>k;
//添加关系边
if(fa!=-1)
ed[cnt++]={id,fa};
if(mo!=-1)
ed[cnt++]={id,mo};
ex[id]=true;//标记该人存在
for(int j=0;j<k;j++){
cin>>child;
ed[cnt++]={id,child};
}
cin>>ho[id]>>area[id];
}
for(int i=0;i<cnt;i++){
join(ed[i].a,ed[i].b);
}
vector<family> families;
for(int i=0;i<N;i++){
if(ex[i]&&re[i]==i){//如果此人真实存在且为根
families.push_back({i,pe[i],ho[i],area[i]});
}
}
sort(families.begin(),families.end());//排序
cout<<families.size()<<endl;
for(int i=0;i<families.size();i++){
cout<<setw(4)<<setfill('0')<<families[i].id<<" "<<families[i].pp<<" "<<fixed<<setprecision(3)<<families[i].hnum*1.0/families[i].pp<<" "<<families[i].anum*1.0/families[i].pp<<endl;
}
return 0;
}
小结
涉及到集合问题可以想到用并查集
如若输入不是标准关系式,则将其转换为关系式,然后加入到并查集中
对于此题中要求的,输出家庭中最小成员,人数,人均房数,人均面积可以以并查集的根为最小成员id,用数组记录下每个人的房数和面积,在加入的时候全都累加到数组以根为下标的值上