PAT天梯赛练习 L3-003 社交集群 (30分) DFS搜索
题目分析:
一共有N个编号为1~1000的人,以及一共有编号为1~1000种不同的兴趣,在题目给出1~N编号的人员每个人喜欢的兴趣的id后,要求统计出不同的人员集合的个数以及每个人员几个的人数从大到小输出
注意点:
1.每个输入的人员id范围1~N,但是并不意味着兴趣的id也是1~N,完全有可能是大于N 小于等于1000的其他数
2.通过样例输出我们可以得知,如果有三个人喜欢的是(2,3)(3,5)(5,6)类似这样的三个人是归属一个集合的,换句话说只要某个人和另一个集合中的任何一个人有兴趣的交集,就可以加入这个集合,并不是要求集合中的所有人都要有至少一个的共同兴趣才行
3.在查询的时候避免人员id与兴趣id的混淆
题解:
这里我的方法是用两个vector数组,一个存放id为i的人员喜欢的兴趣的id,另一个存放id为i的兴趣所拥护者的id(我称喜欢i兴趣的人为i兴趣的拥护者),由于最后的答案是输出不同的集合数量以及每个集合的人数,这里我预先规定每个人员集合中有一个领导者,p[i]为id为i的人所属集合的领导者的id,在搜索合并集合之前,每个人都是独立的个体集合,我们的搜索通过dfs实现,出发点是每个人的id,通过查询编号为id的人所喜欢的所有的兴趣,然后进一步查询喜欢这个兴趣的所有人的id,而此时这些人都是和前者有交集的,则p[当前id]就是搜索的起始点的人员的id,然后我们继续查询此时这个人喜欢的兴趣,然后进一步查询这个兴趣所有的拥护者,这些拥护者p[i]也要赋值dfs最初的那个人的id......依次重复(这个过程通过代码的观察更为直观),要注意的是搜索的过程中需要不断将被搜到的新的人员记录下来,每次只深入搜索未搜过的点
当你将所有的人员通过p[i]分成一个一个的集合,每个用户p[i]为所属集合的领导者id后,无论是统计人数还是统计集合数都将迎刃而解
代码:
1 #include<iostream> 2 #include<stdio.h> 3 #include<vector> 4 #include<set> 5 #include<string.h> 6 #include<cmath> 7 #include<algorithm> 8 using namespace std; 9 10 const int N = 1005; 11 int p[N], n; //p[i]为i属于的集群的首领的id 12 int vis[N]; //i是否已经被分类 13 vector<int> v1[N]; //每个兴趣的拥护者id 14 vector<int> v2[N]; //每个人喜欢的兴趣id 15 int ans[N]; 16 17 void dfs(int ori, int now, int type){ 18 // printf("ori = %d now = %d type = %d\n", ori, now, type); 19 if(type == 2){ 20 for(int i = 0; i < v2[now].size(); i++){ 21 dfs(ori, v2[now][i], 1); 22 } 23 }else{ 24 for(int i = 0; i < v1[now].size(); i++){ 25 int t = v1[now][i]; 26 if(vis[t] == 0){ 27 vis[t] = 1; 28 p[t] = ori; 29 dfs(ori, t, 2); 30 } 31 } 32 } 33 } 34 35 bool cmp(int x, int y){ 36 return x > y; 37 } 38 39 int main(){ 40 memset(vis, 0, sizeof(vis)); 41 scanf("%d", &n); 42 for(int i = 1; i <= n; i++){ 43 int m; 44 scanf("%d:", &m); 45 for(int j = 1; j <= m; j++){ 46 int x; 47 scanf("%d", &x); 48 v2[i].push_back(x); 49 v1[x].push_back(i); 50 } 51 } 52 for(int i = 1; i <= n; i++) p[i] = i; //每个人的管理者id 一个集群中所有人以其中一人id为id 53 for(int i = 1; i <= n; i++){ 54 if(vis[i] == 0){ 55 vis[i] = 1; 56 dfs(i, i, 2); //初始人员id 当前id 第三个变量为1则当前id为兴趣id,查拥护者 57 } // 为2则当前id为拥护者id 查拥护者喜欢的兴趣 58 } 59 int sum = 0; 60 memset(ans, 0, sizeof(ans)); 61 for(int i = n; i >= 1; i--){ 62 if(p[i] == i){ 63 sum++; 64 } 65 ans[p[i]]++; 66 } 67 sort(ans+1, ans+n+1, cmp); 68 printf("%d\n", sum); 69 for(int i = 1; i <= sum; i++){ 70 if(i != 1) printf(" "); 71 printf("%d", ans[i]); 72 } 73 printf("\n"); 74 return 0; 75 }