一、概述
题设太麻烦了,就是用并查集。
唯一的误区就在于,我错误理解了并查集的意思。我本以为,并查集执行完所有合并操作后,属于同一个集合的元素,他们的father数组对应的值都相等,实际上,是这些元素的fingFather的返回值都相等。这个错误让我debug了好久。难过。
二、分析
首先是观察数据。数据有
本人、父母、孩子、房产数量、房产面积。
考虑使用两个数组,一个是father数组,负责维护各节点关系,一个是节点数组,负责储存节点信息。
初始化father数组。每个人的father都是他自己。
for(int i=0;i<10010;i++)
{
father[i]=i;
}
并查集,简而言之就是根据某些联系把具有联系的节点放进一个集合,这里的联系,指的就是父母联系和孩子联系。于是,在每个节点输入时,输入父亲,要与本人Union一次,输入母亲,孩子,也都要Union一次。而节点数组,则只需要储存房产数量和房产面积即可。
father数组和节点数组均采用固定大小。如下输入:
int N;
scanf("%d",&N);
set<int> member;
for(int i=0;i<N;i++)
{
int people,dad,mom;
scanf("%d %d %d",&people,&dad,&mom);
member.insert(people);
if(dad!=-1)
{
member.insert(dad);
Union(people,dad);
}
if(mom!=-1)
{
member.insert(mom);
Union(people,mom);
}
int childnum;
scanf("%d",&childnum);
for(int j=0;j<childnum;j++)
{
int childname;
scanf("%d",&childname);
member.insert(childname);
Union(childname,people);
}
if(dad!=-1)
{
member.insert(dad);
Union(people,dad);
}
if(mom!=-1)
{
member.insert(mom);
Union(people,mom);
}
//Union(people,dad);//*************
//Union(people,mom);//**********
double set,area;
scanf("%lf %lf",&set,&area);
node[people].set=set;
node[people].area=area;
}
注意到一点,由于我的节点数组都是采用固定大小,因此还需要一个数组储存本次样例中的所有人,不然没法在一万个节点里面把我需要的拿出来。选择在输入本人、父母、孩子时将其存入一个set。不能选择vector,因为一个人可能既是父母又是孩子,这样可能存储多次导致重复。这要注意。把所有涉及人员存入member set。
Union函数如下:
void Union(int a,int b)
{
int f1=Findfather(a);
int f2=Findfather(b);
if(f1<f2)
father[f2]=f1;
else if(f1>f2)
father[f1]=f2;
}
平时的Union函数只需要判断f1!=f2即可,但是本题要求父亲是最小的那个,因此选择令较小的当父亲。
Findfather函数如下,使用了压缩路径:
int Findfather(int root)
{
int real=root;
while(root!=father[root])
root=father[root];
while(real!=father[real])
{
int temp;
temp=real;
father[temp]=root;
real=father[real];
}
return real;
}
这里就是我最开始一直蛋疼的地方了。我发现无论如何,都没法保证同一个集合的所有元素,他们的father数组都是一个值。举例如下:
a的父母为b、c,孩子为e、f,那么假设abce由小到大为cbaef,在Union之后,a、b、c、e、f的father值分别为:
a与b合并,F(a)=b,F(b)=b;
a与c合并,F(a)=b,F(b)=c,F(c)=c;
a与e合并,F(a)=b,F(b)=c,F(e)=c;
a与f合并,F(a)=b,F(b)=c,F(f)=c。
可以看出来,执行五次Union操作之后,它们形成一棵树,根为c,但是并不是树高为2,而是3,因为a的father一直是b,而b的father是c。这说明的确无法更改直接父亲b,但是可以找到最终父亲,都是c。
这一点明确之后,下面的就好做了。
找到father数组中所有的a=F(a)的,他们就是各自的一家之主。共有多少家庭也就找到了。
vector<int> host;
set<int>::iterator it;
for(it=member.begin();it!=member.end();it++)
{
//printf("%04d父亲是%04d\n",*it,father[*it]);
if(*it==father[*it])
{
host.push_back(*it);
}
}
然后遍历member set,开一个家庭结构体数组,用于储存每个家庭的财产信息。使用Findfather找出是一个家庭的成员并相加。
如下:
for(it1=host.begin();it1!=host.end();it1++)
{
int numsum=0;
double setsum=0;
double areasum=0;
set<int>::iterator it2;
for(it2=member.begin();it2!=member.end();it2++)
{
if(Findfather(*it2)==*it1)//注意是Findfather(*it2)而不是father(*it2)
{
numsum++;
setsum+=node[*it2].set;
areasum+=node[*it2].area;
}
}
family[familynum].father=*it1;
family[familynum].num=numsum;
family[familynum].area=areasum/numsum;
family[familynum].set=setsum/numsum;
familynum++;
}
然后对此结构体数组排序并输出即可。
三、总结
很经典的并查集问题,考察并查集性质。
PS:代码如下:
#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
int father[10010];
struct Node
{
double set=0;
double area=0;
}node[10010];
struct Family
{
int father;
int num=0;
double set=0;
double area=0;
}family[10010];
int Findfather(int root)
{
int real=root;
while(root!=father[root])
root=father[root];
while(real!=father[real])
{
int temp;
temp=real;
father[temp]=root;
real=father[real];
}
return real;
}
void Union(int a,int b)
{
int f1=Findfather(a);
int f2=Findfather(b);
if(f1<f2)
father[f2]=f1;
else if(f1>f2)
father[f1]=f2;
}
bool cmp(Family a,Family b)
{
if(a.area!=b.area)
return a.area>b.area;
else
return a.father<b.father;
}
int main()
{
for(int i=0;i<10010;i++)
{
father[i]=i;
}
int N;
scanf("%d",&N);
set<int> member;
for(int i=0;i<N;i++)
{
int people,dad,mom;
scanf("%d %d %d",&people,&dad,&mom);
member.insert(people);
if(dad!=-1)
{
member.insert(dad);
Union(people,dad);
}
if(mom!=-1)
{
member.insert(mom);
Union(people,mom);
}
int childnum;
scanf("%d",&childnum);
for(int j=0;j<childnum;j++)
{
int childname;
scanf("%d",&childname);
member.insert(childname);
Union(childname,people);
}
if(dad!=-1)
{
member.insert(dad);
Union(people,dad);
}
if(mom!=-1)
{
member.insert(mom);
Union(people,mom);
}
//Union(people,dad);//*************
//Union(people,mom);//**********
double set,area;
scanf("%lf %lf",&set,&area);
node[people].set=set;
node[people].area=area;
}
vector<int> host;
set<int>::iterator it;
for(it=member.begin();it!=member.end();it++)
{
//printf("%04d父亲是%04d\n",*it,father[*it]);
if(*it==father[*it])
{
host.push_back(*it);
}
}
int familynum=host.size();
printf("%d\n",familynum);
familynum=0;
vector<int>::iterator it1;
int setnum=member.size();
for(it1=host.begin();it1!=host.end();it1++)
{
int numsum=0;
double setsum=0;
double areasum=0;
set<int>::iterator it2;
for(it2=member.begin();it2!=member.end();it2++)
{
if(Findfather(*it2)==*it1)//注意是Findfather(*it2)而不是father(*it2)
{
numsum++;
setsum+=node[*it2].set;
areasum+=node[*it2].area;
}
}
family[familynum].father=*it1;
family[familynum].num=numsum;
family[familynum].area=areasum/numsum;
family[familynum].set=setsum/numsum;
familynum++;
}
sort(family,family+familynum,cmp);
for(int i=0;i<familynum;i++)
{
printf("%04d %d %.3lf %.3lf\n",family[i].father,family[i].num,family[i].set,family[i].area);
}
}