这道题一开始是想使用并查集去做,但是虽然并查集可以将属于同一家庭的人合并到一块,但是这道题目要考虑维护其它信息,在并查集进行合并的时候,可能被合并的成员的信息还没有输入(其实理论上应该也能做),所以我换了一种更加好想的方法。
25分的正解是使用 建图 加 bfs 来做的。属于同一个家庭的两个人(题目中用编号代替),我们在他们之间建立双向边 。然后遍历每一个人对其使用bfs算法,统计这个人家庭的相关统计信息。同时在bfs算法过程中会对同一家庭中的其他人进行标记,所以当我们遍历过程中这个人被标记过了,那么就跳过这个人。然后得到每一个家庭的统计信息,将其按照题目要求输出。
还要注意两个点:其一,由于我们是通过枚举整个编号空间来处理的,那么只考虑输入中提到的编号,所以将编号空间的st数组先初始化为0x3f3f3f3f,然后将输入编号对应st中元素初始化为0。
其二,关于测试点5。由于pta中“4位地址”的含义包括 像0001 0000 这种情况,所以我们在枚举编号空间时,要从0开始枚举(这都卡,好严谨qaq)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10,M = N * 20;
int h[N],e[M],ne[M],idx;
int cnt[N];
int area[N];
int n ;
int st[N];
struct FAM{
int add;
int num;
double cnt;
double area;
bool operator<(struct FAM t)const{
if(area==t.area){
return add<t.add;
}
return area>t.area;
}
}fam[N];
int top ;
void add(int a, int b){
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}
void bfs(int x){
int fcnt = cnt[x];
int farea = area[x];
int fnum = 1 ;
int add = x;
queue<int> q;
q.push(x);
st[x] = 1;
while(q.size()){
int t = q.front();
// cout << x << ' ' << t << endl;
q.pop();
for(int i= h[t];i!=-1;i=ne[i]){
int j = e[i];
if(st[j]) continue;
fcnt += cnt[j];
farea += area[j];
fnum += 1;
add = min(add,j);
q.push(j);
st[j] = 1;
}
}
fam[top++] = {add,fnum,fcnt * 1.0 / fnum,farea * 1.0 /fnum};
}
int main (){
cin >> n ;
memset(h,-1,sizeof h);
memset(st,0x3f,sizeof st);
for(int i=1;i<=n;i++){
int addr,fadd,madd;
int chadd[10];
int k ;
cin >> addr >> fadd >> madd;
st[addr] = st[fadd] = st[madd] = 0 ;
if(fadd!=-1){
add(addr,fadd);
add(fadd,addr);
}
if(madd!=-1){
add(addr,madd);
add(madd,addr);
}
cin >> k ;
for(int i=1;i<=k;i++){
cin >> chadd[i];
st[chadd[i]] = 0 ;
add(addr,chadd[i]);
add(chadd[i],addr);
}
int tcnt,tarea;
cin >> tcnt >> tarea;
cnt[addr] = tcnt;
area[addr] = tarea;
}
for(int i = 1;i<=9999;i++){
if(st[i]) continue;
bfs(i);
}
cout << top << endl;
sort(fam,fam+top);
for(int i=0;i<top;i++){
auto t = fam[i];
printf("%04d %d %.3lf %.3lf\n",t.add,t.num,t.cnt,t.area);
}
return 0;
}