1114 Family Property (25 分)

PAT甲级114
点击上面是题目

题意给出n个家庭信息,家庭的家长id 父亲的id 母亲的id k个孩子的id 一共有几套房 房子的总面积
要求求出一个家族中编号id最小的人,家族中的总人数, 家族的每个人平均房产面积,以及平均个人拥有的房屋套数。
答案按照平均面积的非升序输出,如果有平局,按照最小id成员的非降序输出。

我是用连同块做的,我直接把父母也当作孩子了,然后构建了一个无向图,这样孩子可以到达父母,父母也可以到达孩子,只要能够达到就说明是一个家族的,这样用dfs遍历一个块统计结点数就可以了,然后再统计的同时计算房屋总面积,总套数,以及成员数。

我觉得我的解法好像有点不是很正规,因为我把父母当作孩子来处理了,我总觉得有点怪怪的。。。。。。
代码注释比较详细:
补充一下,我刚刚觉得自己的解法不是正解,看了一下其他人的解法,发现我的还是不错的,由于是连同块问题所以可以用dfs也可以用并查集,并查集我开始想到了,然后在谁当父亲结点以及怎么记录一个块的最小结点的问题上,还有如果把资源合并的问题上没想明白,只想到了dfs来做,可能是思维模式没有这么想过吧

后面会补充到并查集的做法,代码贴在我dfs解法下面

#include<bits/stdc++.h>
using namespace std;
const int N = 10001;
int n;
struct node { // 图
    vector<int> child_id;
    int sets;
    int area;
    node() {
        sets = area = 0;
    }
}tree[N]; //就是一个图
    
struct famlly { //用来记录每一个家族的信息
    int min_id;
    int members;
    double area;
    double sets;
    double avg_areas;
}temp;   

vector<famlly> path; // 家族信息的集合
bool vis[N];
int vislist[N];

bool cmp(const famlly &a, const famlly &b) {
	if(a.avg_areas != b.avg_areas) return a.avg_areas > b.avg_areas;
	else return a.min_id < b.min_id;
}

void DFS(int u) { 
    if(vis[u] == false) {
        temp.members++; //这是一个新的家庭成员 记录一波
        tree[u].area != 0 ? temp.area += tree[u].area : 2333333;
        tree[u].sets != 0 ? temp.sets += tree[u].sets : 2333333;
    }
    vis[u] = true;   //防止重复计算
    u < temp.min_id ? temp.min_id = u : 233; //刷新一下最小编号的家庭成员
    for(int i = 0; i < tree[u].child_id.size(); i++) { //看看这个成员是否可以做出贡献 探出其他的家庭成员
        int v = tree[u].child_id[i]; //拿到可能没访问的家庭成员 
        if(vis[v] == false) { //确定该成员是否有必要访问
            DFS(v);  //访问他
        }
    }
}

void DFSTrave() { 
    memset(vis, false, sizeof(vis));  //这个数组只用初始化一次
    for(int i = 0; i < n; i++) { 
        int u = vislist[i]; //只用从有房的的结点开始遍历就可以了,就是看看与其他有房的块连同不连同,
        if(vis[u] == false) { //因为有房的家庭题目都给出了,所有不存在从有房的结点遍历会漏人的情况
            temp.min_id = 0x3fffffff;  //状态初始化
            temp.members = 0;
            temp.area = 0;
            temp.sets = 0;
            temp.avg_areas = 0;
            DFS(u);
            temp.avg_areas = temp.area / temp.members;  
            temp.sets = temp.sets / temp.members;
            path.push_back(temp); //记录得到的家庭信息
        }
    }
}

int main() {
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        int id, f, m, k, area, child_id;
        scanf("%d %d %d %d", &id, &f, &m, &k);
        vislist[i] = id;                       //这里我直接把树当作图来做了,我把父母都当作孩子。。。。尴尬
        for(int i = 0; i< k; i++) {     //并且构造成无向图,转化为连同块来做
            scanf("%d", &child_id);
            tree[id].child_id.push_back(child_id);
            tree[child_id].child_id.push_back(id);
        }
        if(f != -1) {
            tree[id].child_id.push_back(f);
            tree[f].child_id.push_back(id);
        }
        
        if(m != -1) {
             tree[id].child_id.push_back(m);
             tree[m].child_id.push_back(id);
          }
          scanf("%d %d", &tree[id].sets, &tree[id].area);
        }
       DFSTrave();
       sort(path.begin(), path.end(), cmp);  //排序
       printf("%d\n", path.size());
       for(int i = 0; i < path.size(); i++) {
       		printf("%04d %d %.3lf %.3lf\n", path[i].min_id, path[i].members, path[i].sets, path[i].avg_areas);
	   }
    return 0;
} 

并查集解法:
思路,就是把所有的人都合并,看能划分几个块,每个块都是一个家族,然后在合并的时候动一下手脚,让id小的当族长。然后划分出来的家族家族中id小的是族长。
然后遍历一遍所有的结点,出现过的结点查询他的父亲,查到的父亲一定是他的族长,然后把资源合并到族长身上,最后,按照族长的资源排序。就行了。
代码附上:

#include<bits/stdc++.h>
using namespace std;
const int N = 10001;
const int INF = 0x3fffffff;
int Father[N];
bool vis[N];


struct famlly { //有用的功能就是记录一下家长的信息
    int sets;
    int areas;
    famlly() {
        areas = sets = 0;
    }
}famlly_info[N];

struct node { //记录一下族长们的信息
    int famlly_head_id;
    double sets;
    double areas;
    int members;
    bool is_famlly_head;
    node() {
        sets = areas = members = 0;
        is_famlly_head = false;
    }
}info[N];

vector<node> famlly_head; //家族信息 //从族长表筛选出族长方便排序输出,后面想了一下其实没必要直接排序族长表就行了。。。。

int Init() {
    for(int i = 0; i < N; i++) {
        Father[i] = i;
    }
}

int Find_Father(int x) {
    int a = x;
    while(x != Father[x]) {
        x = Father[x];
    }
    while(a != Father[a]) {
        int z = a;
        a = Father[a];
        Father[z] = x; //路径压缩
    }
    return x;
}

void Union(int a, int b) {
    int Fa = Find_Father(a);
    int Fb = Find_Father(b);
    if(Fa < Fb) {  //谁小谁是爸爸
        Father[Fb] = Fa;
    } else {
        Father[Fa] = Fb;
    }
}

bool cmp(node a, node b) {
    if(a.areas / a.members != b.areas / b.members) return a.areas / a.members > b.areas / b.members; //由于不想在结构体里面申请元素,就写成这样了。。。
    else return a.famlly_head_id < b.famlly_head_id;
}

int main() {
    int n;
    scanf("%d", &n);
    Init();
    for(int i = 0; i < n; i++) {
        int id, F_id, M_id, k, child_id, sets, areas;
        scanf("%d %d %d %d", &id, &F_id, &M_id, &k);
        vis[id] = true;
        if(F_id != -1) {               //这里把所有人按家族合并好了,并且让id最小的当上了族长哈哈哈哈!!!
            Union(F_id, id);
            vis[F_id] = true;
        } 
        if(M_id != -1) {                  //这里总体来说就是把一家人全合并而且小的当族长 
            Union(M_id, id);
            vis[M_id] = true;
        }
        for(int j = 0; j < k; j++) {
            scanf("%d", &child_id);
            Union(child_id, id);
            vis[child_id] = true;
        }
        scanf("%d %d", &sets, &areas);
        famlly_info[id].sets = sets;  //财产记录在家长名下
        famlly_info[id].areas =areas;
    }
    for(int i = 0; i < N; i++) { //查找族长,并且把财产总和到族长名下
        if(vis[i] == true) {
            int F = Find_Father(i);
            info[F].areas += famlly_info[i].areas;
            info[F].sets += famlly_info[i].sets;
            info[F].members++;
            info[F].is_famlly_head = true; //这个货是族长
        }
    }
    for(int i = 0; i < N; i++) {
        if(vis[i] == true && info[i].is_famlly_head == true) { //筛选出族长
			info[i].famlly_head_id = i;
            famlly_head.push_back(info[i]);
        }
    }
    sort(famlly_head.begin(), famlly_head.end(), cmp);
    printf("%d\n", famlly_head.size());
    for(int i = 0; i < famlly_head.size(); i++) {  //这里的可读性不高哈, 反正就这个意思,按照题目意思输出就完事了
        printf("%04d %d %.3lf %.3lf\n", famlly_head[i].famlly_head_id, famlly_head[i].members, famlly_head[i].sets / famlly_head[i].members, famlly_head[i].areas / famlly_head[i].members);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值