PAT_A1114
PAT甲级1114.
目录
A1114
题目
样例
输入:
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
思路和坑点
思路:
此题信息量较大,有一定的的阅读理解难度。题目的输入首先是正整数N,表示将要提供的人员数量。接下里N行,每行包含一个人的个人信息,其中依次包括个人身份编号、父亲编号、母亲编号、孩子个数、对应每个孩子的编号,个人房产套数,个人房产总面积。题目要求的输出结果是,输出家族个数(直系或者旁系的人都算为一个家族,也就是需要合并得到家族并查集),然后每行输出一组家族信息,包括最小成员的编号,家族总人数,平均房产套数,平均房产面积,要求输出的平均值精确到三位小数。
主要是需要处理的信息比较多,这里给出的思路是,凡是有关系的人如果还未合并都进行合并统计,统计包括:最小编号、房产等信息,同时还要通过数组保存并查集信息。具体实现见代码。
坑点:
注意每个信息的含义,以及输出要求。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef struct data{
int id,fid,mid,num,area; //记录每一条个人信息,其中包括个人账号id,父母的id,房产套数num,房产面积area
vector<int> cid; //孩子数组
int minid; //minid用于存放此人所在家族中最小的id号
}Data;
typedef struct node{
int id,people; //最小id 成员个数
double num,area; //排序和输出的数据
bool flag; //是否是根 标记 ,初始化为否,如果有家组信息,则改为true
node(){
id=10000; flag=false; //id初始化为10000也就是最大值,然后和家族中每一条记录比较,如果有更小,则更新
}
}Node;
Data all[1005]; //数据数组
Node ans[10000]; //答案数组,因为个人编号4位,所以代表可能存在的任务有10000个
int father[10000]; //并查集father数组,初始化为-1
int find(int x){ //查找所在编号x所在并查集的父节点
while(father[x]>=0){
x=father[x];
}
return x;
}
void Union(int a,int b){ //并查集的合并函数
int fa=find(a); //找到两个人所在家庭的根
int fb=find(b);
if(fa!=fb){ //如果两人不在同一个家族则进行合并,为了减小并查集路径,将小的合并在大的上边
if(father[fa]>father[fb]){
father[fb]+=father[fa];
father[fa]=fb;
}
else{
father[fa]+=father[fb];
father[fb]=fa;
}
}
}
bool cmp(Node a,Node b){ //排序函数
if(a.area!=b.area) return a.area>b.area;
else return a.id<b.id;
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("1.txt", "r", stdin);
#endif
int n,k,cnt=0; //n表示数据个数,k表示每一个人的孩子个数,cnt
scanf("%d",&n);
//读入数据并构建并查集关系
fill(father,father+10000,-1); //初始化并查集
for(int i=0;i<n;i++){ //读入数据
scanf("%d%d%d%d",&all[i].id,&all[i].fid,&all[i].mid,&k);
all[i].minid=all[i].id; //暂时初始化每个人所在家庭中最小的id是自己
if(all[i].fid!=-1){ //如果有父亲,就把自己和父亲所在家族合并
Union(all[i].id,all[i].fid);
if(all[i].fid<all[i].minid) //比较所有亲属的id,将自己的minid更新到最小,凡是遇到亲属就更新,这样每个人都能保证minid是家族中最小的
all[i].minid=all[i].fid; //这里更具父亲的id,更新minid
}
if(all[i].mid!=-1){ //对母亲采取和父亲同样的处理办法
Union(all[i].id,all[i].mid);
if(all[i].mid<all[i].minid)
all[i].minid=all[i].mid;
}
for(int j=0;j<k;j++){ //对儿子也采取和父亲同样的处理办法
int temp;
scanf("%d",&temp); //儿子需要记录在数组中
all[i].cid.push_back(temp);
Union(temp,all[i].id);
if(temp<all[i].minid)
all[i].minid=temp;
}
scanf("%d%d",&all[i].num,&all[i].area);
}
//数据统计计算
for(int i=0;i<n;i++){ //将每一个人的房产数据统计进入他所在的家庭组
int id=find(all[i].id); //找到所在家族(并查集)的根
if(all[i].minid<ans[id].id) //用自己小家族的minid更新整个家族的minid
ans[id].id=all[i].minid;
ans[id].num+=all[i].num; //累加房产套数
ans[id].area+=all[i].area; //累加房产面积
ans[id].flag=true; //更新标记
}
for(int i=0;i<10000;i++){ //遍历答案数组并计算最终的数值
if(ans[i].flag){ //对于是家族根的更新相关数据
cnt++; //统计家族个数
ans[i].people=-father[i];//统计家族人数和平均房产数量、平均房产面积
ans[i].num=(double) (ans[i].num*1.0/ans[i].people);
ans[i].area=(double)(ans[i].area*1.0/ans[i].people);
}
}
sort(ans,ans+10000,cmp); //按照规则进行排序
printf("%d\n",cnt);
for(int i=0;i<cnt;i++){ //输出最后的结果
printf("%04d %d %.3f %.3f\n",ans[i].id,ans[i].people,ans[i].num,ans[i].area);
}
return 0;
}