题目粗心:
给出若干人之间的通话长度,依照这些通话将他们分成若干个组。当初给定一个犯罪团伙,而该组内点权最大的人视为喽罗。要求输入犯罪团伙的个数,并且依照喽罗姓名字典序从小到大的程序输入每个犯罪团伙的喽罗姓名和成员个数。
算法思路:
这道题的题意得先了解分明,
In each gang, the one with maximum total weight is the head.
这句话的意思是$maximum$ $total$ $weight$的点为$head$,然而$weight$在题目中是边权,所以这里针对题目的案例进行手动绘图和假如后,比拟正当的解释就是一个点的$total$ $weight$指的是该点出边和入边的边权的和。并且依据语义来说,该图应该是一个无向图,因为通话是建设单方的连贯.
接下来咱们要晓得咱们须要取得那些信息能够求解这些问题,以及通过什么形式取得这些数据,题目要求输入每个犯罪团伙的喽罗姓名和成员个数,首先犯罪团伙的定义是一个团伙(连通重量)其人数大于2并且总边权的和大于K,而后头目标定义是组内点权最大的人视为喽罗,那么咱们要取得信息如下:
1)组内的人数(连通重量的结点个数) 2)组内的通话时间总和(连通重量的总边权) 3)组内通话时间最长的人(连通重量中点权最大的结点编号)
当初晓得了须要取得下面3条信息后,就得晓得如何取得3条信息,这些信息都与连通重量无关,在图中咱们取得每个结点或者连通重量信息的办法就是遍历,这里应用了DFS。
在进行DFS遍历之前得先对数据进行预处理:
- 输出的名字是字符串,咱们遍历的是结点编号,同时输入的时候也是字符串,那么得建设名字--->结点编号,结点编号----->名字的双向映射,这里应用
unordered_map<int,string> indexToName
存储编号到姓名的映射,unordered_map<string,int> nameToIndex
存储姓名到编号的映射,具体的做法就是先应用numPerson=0
示意以后已知结点的个数,同时也是新退出图中的结点的编号,在每次输出数据的时候先判断以后人的名字是否呈现过,如果没有呈现就建设名字和结点编号的映射和编号和名字的反映射,否则就取出该名字的编号,紧接着就依据编号,累计点权和边权。
DFS遍历过程:
每一次DFS须要取得以上3条信息并且每次遍历一个连通重量,咱们设置参数start,number,total_time,head
别离代表以后拜访结点,以后连通重量的结点个数,边权总和和最大点权的编号,每次拜访结点就累加连通重量的结点个数++number
,并且判断以后结点的点权和head
的点权哪个更大,如果以后结点更大,更新head
,而后再遍历其邻接点,这里有个留神点,因为有可能是有环的图,所以为了计算总边权,咱们先累加以后结点和邻接点的边权,而后再判断邻接点是否能够拜访。并且每一条边只能拜访一次,所以在每次拜访后,就将该条边进行赋值为0,这样保障在反复拜访的时候总边权计算不会出错,之所以先累计边权,看下图即可:
接下来可就是遍历整个图形取得所有连通重量的那3个信息,对于每一个顶点只有没有拜访就调用DFS拜访该连通重量,而后将取得的信息进行解决,只有number>2&&total_time>K
阐明是犯罪团伙,那么记录犯罪团伙的喽罗和人数,这里采纳map<string,int> result
记录,因为能够主动排序,具体操作为result[indexToName[head]] = number;
最初依照要求输入即可
留神点;
- 1.测试点3呈现段谬误,起因在于数组开的太小了,n条边最多有2*n个结点,至多开到2000以上
- 2.没有排序测试点2和5谬误
- 3、没有思考到一个团伙有2个点权一样大输入字典序小的测试点5谬误,间接在遍历图的时候依照结点编号从小到大进行遍历就能够防止
- 4、题目说
A name is a string of three capital letters chosen from A-Z
,并不是名字都是AAA,BBB这种3个字母都一样的名字,只不过样例给的是这样的,有误导作用,不然只能过2个测试点(不要问我是怎么晓得的,(╥╯^╰╥)) ,所以应用从0开始顺次递增赋值编号是最不便的,字符串hash这里不太实用,容易超内存。
提交后果:
很多网上的代码只能通过PAT的测试点,无奈通过牛客网的测试,自己提供的代码,均能通过.
AC代码:
#include <cstdio>#include <vector>#include <iostream>#include <unordered_map>#include <map>using namespace std;int N,K;//边数和阈值bool visited[2000];//拜访标记数组int G[2000][2000];// 邻接矩阵,其值为边权int node_weight[2000];// 点权unordered_map<int,string> indexToName;// 存储编号到姓名的映射unordered_map<string,int> nameToIndex;// 存储姓名到编号的映射map<string,int> result;// 每一个gang的head和人数汇合int numPerson = 0;//总人数void DFS(int start,int &number,int &total_time,int &head){ visited[start] = true;// 拜访该节点 ++number;// 以后连通重量节点数目加一 if(node_weight[head]<node_weight[start]){ // 更新head head = start; } // 遍历每一个领接点 for(int i=0;i<numPerson;++i){ if(G[start][i]!=0){ // i是start的领接点 total_time += G[start][i];// 先累计总时长,为了防止出现环的时候少拜访一条边 // start->i的边只能拜访一遍,得置为0,避免反复计算影响后果 G[start][i] = G[i][start] = 0; if(!visited[i]){ DFS(i,number,total_time,head); } } }}void DFSTraverse(){ for (int i = 0; i < numPerson; ++i) { if(!visited[i]){ int number = 0;// 每一个gang的人数 int total_time = 0;// 每一个gang的总通话时长 int head = i;// 每一个gang的head,初始得是以后拜访连通块中的点,不能是0 DFS(i,number,total_time,head); if(number>2&&total_time>K){ // 以后连通重量是一个gang result[indexToName[head]] = number; } } } printf("%lu\n",result.size()); map<string,int>::iterator it; for(it=result.begin();it!=result.end();++it){ printf("%s %d\n",it->first.c_str(),it->second); }}int main(){ scanf("%d %d",&N,&K); string a,b; int time,index_a,index_b; for (int i = 0; i < N; ++i) { cin>>a>>b>>time; if(nameToIndex.find(a)==nameToIndex.end()){ // 第一次呈现,赋予编号 index_a = numPerson++; nameToIndex[a] = index_a; indexToName[index_a] = a; } else { // 不是第一次呈现,取出编号 index_a = nameToIndex[a]; } if(nameToIndex.find(b)==nameToIndex.end()){ // 第一次呈现,赋予编号 index_b = numPerson++; nameToIndex[b] = index_b; indexToName[index_b] = b; } else { // 不是第一次呈现,取出编号 index_b = nameToIndex[b]; } // 边权 G[index_a][index_b] += time; G[index_b][index_a] += time; // 点权 node_weight[index_a] += time; node_weight[index_b] += time; } DFSTraverse(); return 0;}