All the online templates use the traditional merge-check-collection template. To be honest, I don’t like the merge-check-collection writing method, so after studying for an afternoon, I finally wrote the merge-check-collection writing method that I am satisfied with (actually more like a simulation). And the speed and memory are also excellent.
The key is: [a person's social group] and [the social graph body of all fans of each hobby he likes] belong to the same Social Cluster,
that is to say: let [a person's collection] and [he likes] The collection of all the fans of each hobby of 】Merge (all fans will include themselves, it doesn’t matter if you don’t deal with it or not, skipping will be faster) is our task.
Do not use traditional union search
- Use an
hobby_lover
adjacency list to store all fans of a certain hobby - Use an
person_hobby
adjacency list to store all the hobbies of a certain person - Use
map
to create the[集合编号:集合]
map (namedCluster
), with the first set of people to enter the number as a set of numbers, that isCluster
the key (and can be understood as the investigation focused on the root node)), in check and focus on different there will be a collection of elements intersect , so I usevector
insteadset
of; - Use
setID
an array to record the number of the collection where each person is located (can also be understood as the root node of the combined check collection) - For
findFather()
the operation of finding the root node of the set where a node is located in the combined query set , you can directly use itsetID[某一个人的编号]
(the search is actually the number of the set) ------- and you can find whether two elements are in the same set usedsetID[a] == setID[b]
to determine - And search for two sets of sets
Union()
merge operation, in fact, the two sets ofvector
directpush_back
andpop_back
well takenback
elements can be done at a combined (two sets and check the focus will not intersect the element)
# include <bits/stdc++.h>
using namespace std;
int N;
vector<int> hobby_lover[1010]; // 某一个爱好的每一个爱好者
vector<int> person_hobby[1010]; // 某一个人的每一个爱好
map<int, vector<int> > Cluster; // 以第一个加入集合的人作为集合的编号(键),值为一个社交团体集合
vector<int> setID(1010, 0); // 存放每个人所在的集合编号,为0说明集合不存在(这个人不存在于任何集合)
int main(){
// 输入
cin >> N;
for(int person = 1;person <= N;++person){
int K;
scanf("%d: ", &K);
for(int j = 0;j < K;++j){
int hobby;
cin >> hobby;
hobby_lover[hobby].push_back(person);
person_hobby[person].push_back(hobby);
}
}
// 算法开始
for(int person = 1;person <= N;++person){
// 遍历每一个人
// 【这个人】指代每一次的【person】
// 如果【这个人】没在任何集合那就让它自创一个集合,
// 于是他就一定是集合的根节点,于是它的编号也就是person值就是它所属集合的编号
if(setID[person] == 0){
setID[person] = person;
Cluster[setID[person]].push_back(person);
}
for(int hobby: person_hobby[person]){
// 遍历【这个人】的每一个爱好
// 《某个爱好者》指代每一次的【lover】
for(int lover: hobby_lover[hobby]){
// 遍历【这个人】的某一个爱好的所有爱好者,让【这个人】和对应的爱好者并入一个集合
if(setID[lover] == 0){
// 如果《某个爱好者》没在任何集合,那就让他并入【这个人】所在的集合
setID[lover] = setID[person];
Cluster[setID[person]].push_back(lover);
}
else {
// 如果《某个爱好者》有所在集合,那就将《某个爱好者》所在的集合并入【这个人】所在的集合
if(setID[person] == setID[lover]) continue; // 如果两个要合并的集合是同一个集合,那就跳过
for(int i = Cluster[setID[lover]].size() - 1;i >= 0 ;i--){
int inp = Cluster[setID[lover]].back(); // 取得《某个爱好者》所在集合的成员
Cluster[setID[person]].push_back(inp);
Cluster[setID[lover]].pop_back();
}
Cluster.erase(setID[lover]); // 去掉被合并的集合
setID[lover] = setID[person]; // 合并后记得更新《某个爱好者》所在集合的编号为【这个人】的集合
}
}
}
}
vector<int> rst;
cout << Cluster.size() << endl;
for(auto s: Cluster){
rst.push_back(s.second.size()); // 统计每个社交团体的数量
}
sort(rst.begin(), rst.end(), greater<int>());
for(int i = 0;i < rst.size();++i){
cout << rst[i] << (i == rst.size()-1 ? "\n" : " ");
}
return 0;
}
/*
hobby people
[1] : 7
[2] : 1
[3] : 3 5
[4] : 2 4 6 8
[5] : 3 7
[6] : 7
[7] : 1
[8] : 7
[9] :
[10] : 1
*/
/*
people hobby
1 : 2 7 10
2 : 4
3 : 5 3
4 : 4
5 : 3
6 : 4
7 : 6 8 1 5
8 : 4
*/
Traditional union
At the beginning, everyone is a set, and people with the same hobby are merged
every time. Every time person and hobby_person[hb] have the same hobby hb, so every time they are merged
# include <bits/stdc++.h>
using namespace std;
int N;
int father[1010];
int isRoot[1010];
int hobby_person[1010] = {
0};
int findFather(int v){
if(v == father[v]) return v;
father[v] = findFather(father[v]);
return father[v];
}
void Union(int a, int b){
int fa = findFather(a);
int fb = findFather(b);
if(fa != fb) father[fa] = fb;
}
void init(){
for(int i = 1;i <= N;++i){
father[i] = i;
isRoot[i] = false;
}
}
int main(){
cin >> N;
init();
int K, hb;
for(int person = 1;person <= N;++person){
// 对每个人
scanf("%d: ", &K);
for(int j = 0;j < K;++j){
// 对某个人的每个爱好
cin >> hb;
if(hobby_person[hb] == 0) // 让这个爱好的第一个爱好者成为一个社交圈子
hobby_person[hb] = person;
else // 合并有相同爱好hb的两个社交圈子
// Union(person, hobby_person[hb]); // 这么写也没问题,毕竟在Union函数中都会找到father的
Union(person, findFather(hobby_person[hb]));
}
}
for(int i = 1;i <= N;++i)
isRoot[findFather(i)]++;
int cnt = 0;
for(int i = 1;i <= N;++i)
if(isRoot[i] != 0)
cnt++;
cout << cnt << endl;
sort(isRoot+1, isRoot+N+1, greater<int>());
for(int i = 1;i <= cnt;++i)
cout << isRoot[i] << (i == cnt?'\n':' ');
return 0;
}