1034 Head of a Gang

  大致题意就是给出 N对 人的通话记录,可以根据通话对象分成若干个组(连通图)。在一个连通图中,任意两个人之间的总通话时长表示边权,一个人参与的总通话时长表示点权,所有人的总通话时长表示总边权。现在给定一个阀值K,且只要连通图的总边权超过K,并满足成员数超过2,则该组视为“犯罪团伙”,而且该组内点权最大的人 视为头目。要求输出“犯罪团伙”的个数,并按头目姓名字典序从小到大的顺序输出每个“犯罪团伙”的头目姓名和成员数。

思路:

第一步,建立姓名与编号的映射关系。(难点)

  map<string,int> stringToint;//姓名-编号
  map<int,string> intTostring;//编号-姓名

第二步,保存每个人的点权(一个人参与的总通话时长),保存边权(任意两个人之间的总通话时长)。

(涉及图的DFS时,通常需要一个邻接矩阵(邻接表)、一个顶点表、一个标记顶点是否被访问的数组

  int G[maxn][maxn] = {0}; //保存边权,邻接矩阵

  int weight[maxn] = {0}; //编号-点权,顶点表

  bool visited[maxn] = {false};//标记顶点是否被访问,标记数组

第三步,遍历图的每一个连通块,获取每个连通块的头目、成员数、总边权。

  DFS(int nowVisit,int& head,int& numMember,int& totalValue) //遍历一个连通块

  DFSTrave() ;//遍历图的所有连通块

第四步,通过第三步可以获得连通块的总边权totalValue。如果totalValue 大于给定阀值 K,且成员数大于2,则说明该连通块是一个团伙,将该团伙信息保存下来。

  map<string,int> gang;//头目-成员数

 1 #include<iostream>
 2 #include<vector>
 3 #include<map>
 4 using namespace std;
 5 const int maxn = 2010;
 6 
 7 int G[maxn][maxn] = {0}; //邻接矩阵G,存放边权
 8 int weight[maxn] = {0}; //编号-点权(顶点表)
 9 bool visited[maxn] = {false};//标记是否被访问
10 
11 map<string,int> stringToint;//姓名-编号
12 map<int,string> intTostring;//编号-姓名
13 map<string,int> gang;//头目-成员数
14 
15 int n,k;
16 int numPerson = 0;//编号
17 
18 int change(string str) {
19     if(stringToint.count(str) == 0) {
20         stringToint[str] = numPerson;//姓名-编号
21         intTostring[numPerson] = str;//编号-姓名
22         numPerson++;
23     }
24     return stringToint[str];
25 }
26 void DFS(int nowVisit,int& head,int& numMember,int& totalValue) {//遍历一个连通块
27     numMember++; //成员数加 1
28     visited[nowVisit] = true; //标记nowvisit已访问
29     if(weight[head] < weight[nowVisit]) head = nowVisit;
30     for(int i = 0; i < numPerson; ++i) { //枚举所有人
31         if(G[nowVisit][i] > 0) { //如果能从nowVisit 到达 i
32             totalValue += G[nowVisit][i]; //更新总边权
33             G[nowVisit][i] = G[i][nowVisit] = 0;//删除已访问的边,防止回路遍历
34             if(visited[i] == false) //如果 i未被访问,则递归访问 i
35                 DFS(i,head,numMember,totalValue);
36         }
37     }
38 }
39 
40 void DFSTrave() {//遍历图的所有连通块,获取gang信息
41     for(int i = 0; i < numPerson; ++i) {
42         if(visited[i] == false) {
43             int head = i,numMember = 0,totalValue = 0;//头目、成员数、总边权
44             DFS(i,head,numMember,totalValue);
45             if(numMember > 2 && totalValue > k)
46                 gang[intTostring[head]] = numMember;
47         }
48     }
49 }
50 
51 int main() {
52     cin>>n>>k;
53     string name1,name2;
54     int w;
55     for(int i = 0; i < n; ++i) {
56         cin>>name1>>name2>>w;
57         int id1 = change(name1);
58         int id2 = change(name2);
59         weight[id1] += w;
60         weight[id2] += w;
61         G[id1][id2] += w;
62         G[id2][id1] += w;
63     }
64     DFSTrave(); //遍历图的所有连通块,获取gang信息
65     printf("%d\n",gang.size()); //gang的个数 
66     for(auto it = gang.begin(); it != gang.end(); ++it)
67         cout<<it->first<<" "<<it->second<<endl;
68     return 0;
69 }

 ps:说实话这道题很难,可能是因为刚开始复习图的知识,很多内容需要花时间慢慢熟悉吧。

易混淆点:

涉及图的DFS时,通常需要一个邻接矩阵(邻接表)、一个顶点表、一个标记顶点是否被访问过的数组(因为图的顶点之间关系交错、所以必须要控制顶点只能被访问一次)。

涉及树的DFS时,不需要标记数组,是因为树的结构是自顶向下的,且双亲结点只能向下访问孩子结点,而所以不需要 标记数组。

posted @ 2020-03-06 11:56  tangq123  阅读(170)  评论(0编辑  收藏  举报