1107 Social Clusters (30分)[并查集]

By Jalan

知识工具需求

数学

数据结构和算法

  • 并查集

语言

题干

社会集是一群喷人有相同的兴趣,你要找出所有的社会集
时间给了1200ms

输入条件

N<=1000网中的总人数,
接下来是1-N的N行格式是:
K:H1 H2…
K是兴趣数量,后面是hi是第i个兴趣i[1,1000]

输出条件

社会集数量,每个社会集的人非递增,末尾 不能空格.

例子

例1

输入
8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4
输出
3
4 3 1

题解

第一次(没用并查集)

这个很好理解…因为时间给的很充裕,一般pta给1200ms加上<=1000这种,基本就是你随便玩吧…

思路

  1. 输入
  2. 依据当前的节点遍历相关节点,所有的兴趣都加到hashlist里,直到相关节点不再增加.(证明这种兴趣组不能再拓展)
  3. 排序
  4. 输出

预期时间复杂度

编写用时

30分钟

代码

CPP
#include <bits/stdc++.h>
#include <stdio.h>
#include <vector>

using namespace std;
int n;
vector<vector<int>> hobbies;
vector<bool> known;
vector<int> cluster;
int main(int argc, char const *argv[])
{
    //1
    scanf("%d", &n);
    hobbies.resize(n + 1);
    for (int i = 1, temp; i < n + 1; i++)
    {
        scanf("%d:", &temp);
        for (int j = 0, temp2; j < temp; j++)
        {
            scanf("%d", &temp2);
            hobbies[i].push_back(temp2);
        }
    }
    //2
    known.resize(n + 1);
    cluster.resize(n + 1);
    int clusterCounter = -1;
    for (int i = 1; i < n + 1; i++)
    {
        if (known[i] == false)
        {
            ++clusterCounter;
            vector<int> hashList(1001);
            vector<int> clusterIndex;
            int originSize = 0;
            for (int j = 0; j < hobbies[i].size(); j++)
            {
                hashList[hobbies[i][j]]++;
            }
            for (int j = i; j < n + 1; j++)
            {
                if (known[j] == false)
                {
                    for (int k = 0; k < hobbies[j].size(); k++)
                    {
                        if (hashList[hobbies[j][k]])
                        {
                            known[j] = true;
                            clusterIndex.push_back(j);
                            break;
                        }
                    }
                }
            }
            int finalSize = clusterIndex.size();
            while (finalSize != originSize)
            {
                originSize = finalSize;
                //把本次所有兴趣加到hashList里.
                for (int j = 0; j < finalSize; j++)
                {
                    for (int k = 0; k < hobbies[clusterIndex[j]].size(); k++)
                    {
                        hashList[hobbies[clusterIndex[j]][k]]++;
                    }
                }
                //遍历一次把兴趣一致的加进来
                for (int j = 1; j < n + 1; j++)
                {
                    if (known[j] == false)
                    {
                        for (int k = 0; k < hobbies[j].size(); k++)
                        {
                            if (hashList[hobbies[j][k]])
                            {
                                known[j] = true;
                                clusterIndex.push_back(j);
                                break;
                            }
                        }
                    }
                }
                finalSize=clusterIndex.size();
            }
            cluster[clusterCounter]=finalSize;
        }
    }
    //3
    sort(cluster.begin(),cluster.begin()+clusterCounter+1);
    //4
    printf("%d\n", clusterCounter+1);
    for (int i = clusterCounter; i >=0; i--)
    {
        printf("%d%c", cluster[i], i == 0? '\n' : ' ');
    }

    return 0;
}
运行用时

在这里插入图片描述

第二次(并查集)

思路

首先得明确,在这个并查集里,用于建树的点(就是定点)指的是人的编号,集合之间的联系(边)则抽象成了集合的兴趣直接是否有重复,所以我们可以遍历兴趣建立边.

  1. 输入
  2. 遍历人的兴趣建立边
  3. 遍历边建立并查集树
  4. 遍历树,用map对根做统计
  5. 把map里的数导到vector里排序
  6. 输出

预期时间复杂度

编写用时

30分钟

代码

CPP

#include <vector>

using namespace std;
int n;
vector<vector<int>> hobbies;
typedef struct edge
{
    int x;
    int y;
} edge;
vector<edge> edges;
int findRoot(int vertex, vector<int> &parent)
{
    int temp = vertex;
    while (parent[vertex] != 0)
    {
        vertex = parent[vertex];
    } //循环结束后vertex是根结点编号
    while (parent[temp] != 0)
    {
        int temp2 = temp;
        temp = parent[temp];
        parent[temp2] = vertex;
    } //把路上的节点都梳理到根结点下方
    return vertex;
}
int unionVertex(int va, int vb, vector<int> &parent)
{
    int vaRoot = findRoot(va, parent);
    int vbRoot = findRoot(vb, parent);
    if (vaRoot == vbRoot)
    {
    }
    else
    {
        parent[vaRoot] = vbRoot;
    }
    return 0;
}
int main(int argc, char const *argv[])
{
    //1
    scanf("%d", &n);
    hobbies.resize(n + 1);
    for (int i = 1, temp; i < n + 1; i++)
    {
        scanf("%d:", &temp);
        for (int j = 0, temp2; j < temp; j++)
        {
            scanf("%d", &temp2);
            hobbies[i].push_back(temp2);
        }
    }
    //2
    for (int i = 1; i < n + 1; i++)
    {
        vector<int> hashList(1001);
        for (int j = 0; j < hobbies[i].size(); j++)
        {
            hashList[hobbies[i][j]]++;
        }
        for (int j = i + 1; j < n + 1; j++)
        {
            for (int k = 0; k < hobbies[j].size(); k++)
            {
                if (hashList[hobbies[j][k]])
                {
                    edges.push_back({i, j});
                    break;
                }
            }
        }
    }
    //3
    vector<int> parent(n + 1); //0代表不存在
    for (int l = 0; l < edges.size(); l++)
    {
        int va=edges[l].x;
        int vb=edges[l].y;
        unionVertex(va, vb,parent);
    }
    //4
    unordered_map<int,int> m;
    for (int i = 1; i < n+1; i++)
    {
        int root=findRoot(i, parent);
        m[root]++;
    }
    //5
    vector<int> result;
    for (auto &&i : m)
    {
        result.push_back(i.second);
    }
    sort(result.begin(),result.end());
    //6
    printf("%d\n",result.size());
    for (int i = (int)result.size() - 1; i >= 0; i--)
    {
        printf("%d",result[i]);
        if (i!=0)
        {
            printf(" ");
        }

    }

    return 0;
}

运行用时

在这里插入图片描述

结尾

看在我写了这么多注释的份上可以给我点个赞嘛,求求惹=]砰砰砰,给我加点写下去的油呀@.@
也欢迎关注我的CSDN账号呀,接下来两个月我应该会按这个格式更新所有的PTA甲级题目

                                        **开心code每一天**
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值