【并查集】PAT A1114 Family Property

题目描述

在这里插入图片描述

知识点

并(Union)查(Find)集(Set)(刚好就是并查集的三个核心思想)

实现

码前思考

  1. 看到这种“家族”,发现它是类似于集合的概念的,那么肯定要想到使用并查集
  2. 并查集使用就两个函数 findFatherUnion ,其中 findFather 要注意 路径压缩 即可!
  3. 我的思想是每次读入一行数据就更新一个家族。使用到了map<int,node>,其中int代表家族中最小的成员的idnode则是题中需要的数据结构体;
  4. 我的做法的操作难度主要体现在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;
}

码后反思

  1. 柳婼使用的思想是每次记录一个家庭,最后再记录一个家族,这样的思想更加地清晰,因为划分了两步!
    分析:用并查集。分别用两个结构体数组,一个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;
    }
    
  2. 我使用了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;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值