个人练习-PAT甲级-1107 Social Clusters

题目链接https://pintia.cn/problem-sets/994805342720868352/problems/994805361586847744

题目大意:给出N个人和他们每个人的hobbies,只要两个人有相同的hobby,就属于同一个set。求set的个数,并将所有set的人数从高到低输出。

一开始一直没读懂题目,以为一个人可以属于多个set,然后百思不得其解,因为这样每个set的代表就是一个或多个hobbies。

  • 比如A喜欢1,2,B喜欢1,3,C喜欢3,4,那么就形成了【喜欢1的set】【喜欢3的set】【喜欢4的set】
  • 然而若A喜欢1,2,B喜欢1,2,C喜欢3,4,那么就形成了【喜欢1,2的set】和【喜欢3,4的set】。并且若随后出现了只喜欢4的D,那么set又会变。

要如何区分hobbies组,想破头也没想出来。结果看了柳神的答案后,才知道每个人只属于一个set。也就是说如果A喜欢苹果、香蕉、甜豆腐脑,B喜欢苹果,C喜欢香蕉、咸豆腐脑,那么他们三个也是一个set…真的有点怪。

不过至少题目可以做了。我按照自己的思路做,debug了一下后也AC了,就没抄别人的代码。最后看了看运行结果,似乎不比柳神的差,甚至大多数测试点还快一些。

每一个set由其根节点root代表

  • hb2root,因为hobby的数量没有给出,所以直接取上限1000。下标为hobby,值为root
  • idx2root,下标为人的IDidx,值为根结点root。输入这个人的ID,即可得到他所在的set的根结点
  • root2num,下标为根节点,值为这个set的人数。如果为0,说明它不是根节点。
int hb2root[1001];
vector<int> idx2root;
vector<int> root2num;

最关键的就是之后的循环:

如果读入的hobby没有根节点(也就是这个hobby第一次出现),那么看当前这个人idx的情况——如果idx有根节点(即idx已经找到组织了,他已经属于某一个set了),那么这个hobby的根结点也指向此人idx的根节点。否则,这个人自己成为根节点,并将此hobby的根节点也只想这个人idx,由于此时创建了一个新的set,所以将其大小+1

            if (hb2root[hb] == 0) {
                if (idx2root[idx] != 0)
                    hb2root[hb] = idx2root[idx];
                else {
                    idx2root[idx] = idx;
                    hb2root[hb] = idx;
                    root2num[idx]++;
                }
            }

如果这个hobby已经属于某个根节点了,再看当前这个人idx的情况——如果这个人还没有根节点(不属于某个set),那么将其加入这个hobby的set中。否则,说明这个人已经属于某个set了,且不为这个hobby的set,那么要将这两个不同的set合并。我们将这个人idx所在的set清零,并将所有元素都转移到这个hobby的set中,更改所有元素的根节点。

else {
                if (idx2root[idx] == 0) {
                    idx2root[idx] = hb2root[hb];
                    root2num[hb2root[hb]]++;
                }
                else if (idx2root[idx] != hb2root[hb]) {
                    int tmp_root = idx2root[idx];
                    root2num[tmp_root] = 0;
                    for (int k = 1; k <= N; k++) {
                        if (idx2root[k] == tmp_root) {
                            idx2root[k] = hb2root[hb];
                            root2num[hb2root[hb]]++;
                        }
                    }
                    for (int k = 1; k <= 1000; k++) {
                        if (hb2root[k] == tmp_root)
                            hb2root[k] = hb2root[hb];
                    }
                }
                else;
            }

最后数一数root2num[]中的非零元素,非零元素个数就是set的数目,每个非零元素代表一个set的大小。将非零元素都加入一个结果vectornum[]

    vector<int> num;
    for (int i = 1; i <= N; i++) {
        if (root2num[i] != 0)
            num.push_back(root2num[i]);
    }

num[]排序再输出即可(因为原先的root2num[]元素个数很多,且无用的0很多,排序费时)

    sort(num.begin(), num.end());

    printf("%d\n", num.size());
    for (int i = num.size()-1; i >= 0; i--) {
        if (i == num.size()-1) printf("%d", num[i]);
        else printf(" %d", num[i]);
    }

完整代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <set>

using namespace std;

int hb2root[1001];
vector<int> idx2root;
vector<int> root2num;


int main() {
    int N, K, hb;
    scanf("%d", &N);

    idx2root.resize(N+1, 0);
    root2num.resize(N+1, 0);

    for (int idx = 1; idx <= N; idx++) {
        scanf("%d:", &K);
        for (int j = 0; j < K; j++) {
            scanf("%d", &hb);

            if (hb2root[hb] == 0) {
                if (idx2root[idx] != 0)
                    hb2root[hb] = idx2root[idx];
                else {
                    idx2root[idx] = idx;
                    hb2root[hb] = idx;
                    root2num[idx]++;
                }
            }
            else {
                if (idx2root[idx] == 0) {
                    idx2root[idx] = hb2root[hb];
                    root2num[hb2root[hb]]++;
                }
                else if (idx2root[idx] != hb2root[hb]) {
                    int tmp_root = idx2root[idx];
                    root2num[tmp_root] = 0;
                    for (int k = 1; k <= N; k++) {
                        if (idx2root[k] == tmp_root) {
                            idx2root[k] = hb2root[hb];
                            root2num[hb2root[hb]]++;
                        }
                    }
                    for (int k = 1; k <= 1000; k++) {
                        if (hb2root[k] == tmp_root)
                            hb2root[k] = hb2root[hb];
                    }
                }
                else;
            }

        }
    }

    vector<int> num;
    for (int i = 1; i <= N; i++) {
        if (root2num[i] != 0)
            num.push_back(root2num[i]);
    }

    sort(num.begin(), num.end());

    printf("%d\n", num.size());
    for (int i = num.size()-1; i >= 0; i--) {
        if (i == num.size()-1) printf("%d", num[i]);
        else printf(" %d", num[i]);
    }


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值