1107 Social Clusters (30 points) [PAT Class A non-traditional combined search C++ writing method]

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_loveradjacency list to store all fans of a certain hobby
  • Use an person_hobbyadjacency list to store all the hobbies of a certain person
  • Use mapto create the [集合编号:集合]map (named Cluster), with the first set of people to enter the number as a set of numbers, that is Clusterthe 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 use vectorinstead setof;
  • Use setIDan 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 it setID[某一个人的编号](the search is actually the number of the set) ------- and you can find whether two elements are in the same set used setID[a] == setID[b]to determine
  • And search for two sets of sets Union()merge operation, in fact, the two sets of vectordirect push_backand pop_backwell taken backelements 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;
}

Insert picture description here

/*
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;
}

Guess you like

Origin blog.csdn.net/MYMarcoreus/article/details/113997038