题目描述
知识点
并(Union)查(Find)集(Set)(刚好就是并查集的三个核心思想)
实现
码前思考
- 看到这种“家族”,发现它是类似于集合的概念的,那么肯定要想到使用并查集。
- 并查集使用就两个函数
findFather
和Union
,其中findFather
要注意 路径压缩 即可! - 我的思想是每次读入一行数据就更新一个家族。使用到了
map<int,node>
,其中int
代表家族中最小的成员的id
,node
则是题中需要的数据结构体; - 我的做法的操作难度主要体现在
Union
函数比较复杂。首先判断fu
是否存在于map
中,没有就插入到map
中,然后看fv
是否在map
中,如果在的话,说明其一定是至少有两个家庭成员的!可以想想为什么?之后就是一些需要考虑情况的问题了。。。
代码实现
#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e5+10;
const int maxk = 10;
//父亲数组
int father[maxn];
struct node{
//资产数
int m;
//资产面积
int area;
//成员数
int num;
double avgm;
double avgarea;
int id;
node():m(0),area(0),num(1){}
};
//将根结点映射到具体信息
unordered_map<int,node> mp;
//输入数据
int n;
int findFather(int x){
//采用递归进行实现
if(father[x] == x){
return x;
}else{
int tmp = findFather(father[x]);
father[x] = tmp;
return tmp;
}
}
int Union(int fv,int fu){
//首先找到两者哪一个小
if(fv < fu){
swap(fv,fu);
}
//保证fu一定是最小的那个
//首先检查是否mp中有fu这个结点的数据
if(mp.count(fu) == 0){
//没有则放入
mp[fu] = node();
}
//如果有fv这个值,那么要将它的数据移到fu,并且将它删除
if(mp.count(fv) != 0){
mp[fu].m += mp[fv].m;
mp[fu].area += mp[fv].area;
mp[fu].num += mp[fv].num;
mp.erase(fv);
}else{
//说明是个孤立点,直接加入个数即可
mp[fu].num += 1;
}
//最后设置父亲
father[fv] = fu;
return fu;
}
bool cmp(node a,node b){
return a.avgarea != b.avgarea ? a.avgarea > b.avgarea : a.id < b.id;
}
int main(){
//首先初始化所有的父亲为自身
for(int i=0;i<maxn;i++){
father[i] = i;
}
scanf("%d",&n);
for(int i=0;i<n;i++){
int id[3];
int k;
int children[maxk];
scanf("%d %d %d %d",&id[0],&id[1],&id[2],&k);
for(int j=0;j<k;j++){
scanf("%d",&children[j]);
}
int m;
int area;
scanf("%d %d",&m,&area);
//然后将它们的父亲全部设置成这个最小的结点的父亲
for(int j=1;j<3;j++){
if(id[j] != -1){
int fu = findFather(id[0]);
int fv = findFather(id[j]);
if(fu != fv){
//进行合并
//仍然是大的合并到小的
//需要实时更新fu
Union(fv,fu);
}
}
}
for(int j=0;j<k;j++){
int fu = findFather(id[0]);
int fv = findFather(children[j]);
if(fu != fv){
//进行合并
//仍然是大的合并到小的
Union(fv,fu);
}
}
int fu = findFather(id[0]);
//然后加上数据
mp[fu].m += m;
mp[fu].area += area;
}
vector<node> res;
int len = 0;
//整理一下mp
for(auto pair:mp){
res.push_back(pair.second);
res[len].id=pair.first;
res[len].avgm = (1.0)*res[len].m / res[len].num;
res[len].avgarea = (1.0)*res[len].area / res[len].num;
len++;
}
printf("%d\n",len);
//然后进行排序
sort(res.begin(),res.end(),cmp);
for(int i=0;i<len;i++){
printf("%04d %d %.3f %.3f\n",res[i].id,res[i].num,res[i].avgm,res[i].avgarea);
}
return 0;
}
码后反思
- 柳婼使用的思想是每次记录一个家庭,最后再记录一个家族,这样的思想更加地清晰,因为划分了两步!
分析:用并查集。分别用两个结构体数组,一个data用来接收数据,接收的时候顺便实现了并查集的操作union,另一个数组ans用来输出最后的答案,因为要计算家庭人数,所以用visit标记所有出现过的结点,对于每个结点的父结点,people++统计人数。标记flag == true,计算true的个数cnt就可以知道一共有多少个家庭。排序后输出前cnt个就是所求答案~~#include <cstdio> #include <algorithm> using namespace std; struct DATA { int id, fid, mid, num, area; int cid[10]; }data[1005]; struct node { int id, people; double num, area; bool flag = false; }ans[10000]; int father[10000]; bool visit[10000]; int find(int x) { while(x != father[x]) x = father[x]; return x; } void Union(int a, int b) { int faA = find(a); int faB = find(b); if(faA > faB) father[faA] = faB; else if(faA < faB) father[faB] = faA; } int cmp1(node a, node b) { if(a.area != b.area) return a.area > b.area; else return a.id < b.id; } int main() { int n, k, cnt = 0; scanf("%d", &n); for(int i = 0; i < 10000; i++) father[i] = i; for(int i = 0; i < n; i++) { scanf("%d %d %d %d", &data[i].id, &data[i].fid, &data[i].mid, &k); visit[data[i].id] = true; if(data[i].fid != -1) { visit[data[i].fid] = true; Union(data[i].fid, data[i].id); } if(data[i].mid != -1) { visit[data[i].mid] = true; Union(data[i].mid, data[i].id); } for(int j = 0; j < k; j++) { scanf("%d", &data[i].cid[j]); visit[data[i].cid[j]] = true; Union(data[i].cid[j], data[i].id); } scanf("%d %d", &data[i].num, &data[i].area); } for(int i = 0; i < n; i++) { int id = find(data[i].id); ans[id].id = id; ans[id].num += data[i].num; ans[id].area += data[i].area; ans[id].flag = true; } for(int i = 0; i < 10000; i++) { if(visit[i]) ans[find(i)].people++; if(ans[i].flag) cnt++; } for(int i = 0; i < 10000; i++) { if(ans[i].flag) { 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, cmp1); 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; }
- 我使用了
map
,但是我使用的还不熟练,需要多多加强。
二刷代码
二刷代码的思路更加清晰了~
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1e4+10;
int father[maxn];
unordered_map<int,double> mpe;
unordered_map<int,double> mpa;
unordered_map<int,int> mpp;
void init(){
for(int i=0;i<maxn;i++){
father[i]=i;
mpp[i]=1;
}
return;
}
struct node{
int no;
int cnt;
double avge;
double avga;
};
int findFather(int x){
if(father[x] == x){
return x;
}else{
int F = findFather(father[x]);
father[x] = F;
return F;
}
}
void Union(int u,int v){
int fatherU = findFather(u);
int fatherV = findFather(v);
if(fatherU != fatherV){
//我们要合并到小的那个编号里面
if(fatherU > fatherV){
mpe[fatherV] += mpe[fatherU];
mpa[fatherV] += mpa[fatherU];
mpp[fatherV] += mpp[fatherU];
father[fatherU] = fatherV;
mpe.erase(fatherU);
mpa.erase(fatherU);
mpp.erase(fatherU);
}else{
mpe[fatherU] += mpe[fatherV];
mpa[fatherU] += mpa[fatherV];
mpp[fatherU] += mpp[fatherV];
father[fatherV] = fatherU;
mpe.erase(fatherV);
mpa.erase(fatherV);
mpp.erase(fatherV);
}
}
}
bool cmp(node a,node b){
return a.avga==b.avga ? a.no<b.no : a.avga > b.avga;
}
int main(){
init();
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
//开始读取
vector<int> seq;
int no;
for(int i=0;i<3;i++){
scanf("%d",&no);
if(no!=-1){
seq.push_back(no);
}
}
int k;
scanf("%d",&k);
for(int i=0;i<k;i++){
scanf("%d",&no);
seq.push_back(no);
}
double mestate;
double area;
scanf("%lf %lf",&mestate,&area);
//下面进行合并
int cur=seq[0];
for(int i=1;i<seq.size();i++){
Union(cur,seq[i]);
}
int fatherCur = findFather(cur);
mpe[fatherCur]+=mestate;
mpa[fatherCur]+=area;
}
printf("%d\n",mpe.size());
//然后进行输出
vector<node> res;
for(unordered_map<int,double>::iterator it=mpa.begin();it!=mpa.end();it++){
int no = it->first;
int cnt = mpp[no];
double avge = mpe[no] / cnt;
double avga = mpa[no] / cnt;
node tmp;
tmp.no = no;
tmp.cnt = cnt;
tmp.avga = avga;
tmp.avge = avge;
res.push_back(tmp);
}
sort(res.begin(),res.end(),cmp);
for(int i=0;i<res.size();i++){
printf("%04d %d %.3f %.3f\n",res[i].no,res[i].cnt,res[i].avge,res[i].avga);
}
return 0;
}