题目链接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;
}