题目

One way that the police finds the head of a gang is to check people’s phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls made between the two persons. A “Gang” is a cluster of more than 2 persons who are related to each other with total relation weight being greater than a given threshold K. In each gang, the one with maximum total weight is the head. Now given a list of phone calls, you are supposed to find the gangs and the heads.

Input Specification:

Each input file contains one test case. For each case, the first line contains two positive numbers N and K (both less than or equal to 1000), the number of phone calls and the weight threthold, respectively. Then N lines follow, each in the following format:

Name1 Name2 Time

where Name1 and Name2 are the names of people at the two ends of the call, and Time is the length of the call. A name is a string of three capital letters chosen from A-Z. A time length is a positive integer which is no more than 1000 minutes.

Output Specification:

For each test case, first print in a line the total number of gangs. Then for each gang, print in a line the name of the head and the total number of the members. It is guaranteed that the head is unique for each gang. The output must be sorted according to the alphabetical order of the names of the heads.

Sample Input 1:

8 59

AAA BBB 10

BBB AAA 20

AAA CCC 40

DDD EEE 5

EEE DDD 70

FFF GGG 30

GGG HHH 20

HHH FFF 10

Sample Output 1:

2

AAA 3

GGG 3

Sample Input 2:

8 70

AAA BBB 10

BBB AAA 20

AAA CCC 40

DDD EEE 5

EEE DDD 70

FFF GGG 30

GGG HHH 20

HHH FFF 10

Sample Output 2:

0

题意

警察通过电话记录分析犯罪团伙,有通话记录的人视为有一个关系网,若关系网的总通话时长超过阈值视为犯罪团伙,每个犯罪组织中通话时长最多的视为犯罪头目,求犯罪团伙数目和每个犯罪团伙的头目

已知所有人员、电话记录、阈值

求犯罪团伙数目、每个犯罪团伙的头目名称和成员数

题目分析

  • A和B有电话记录即为有关联---边
  • A和B的关系权重为所有AB相互通话记录(A->B,B->A)的总时长---边权
  • 多个有关联的人员组成一个组,组的权重为所有人员关系权重和---连通分量总边权和
  • 判断一个组是否是犯罪团伙的标准:人数大于2,权重大于已知阈值k
  • 判断一个犯罪团伙的头目标准:点权最大(点权等于所有关联权重和)

已知一个图,求所有连通分量的边权和awt,总人数anum,最大点权wt。判断边权和>k,并且总人数anum>2的连通分量

解题思路

1 存储节点名称,点权,边权

注:可以不使用边权,因为最终统计的是连通分量的总权重,每条边的权重都保存在两边顶点的点权中,最终计算的连通分量总权重是所有边权的两倍

思路 01

注意点:题目已知通话记录为n,若每个通话记录的A,B都不同,最多有2*n个名称(即2*n个顶点),所以maxn=2*n

  • 需要将顶点字符串名称转换为整数,方便操作,使用map<string,int> si存储<名称,id>,使用map<int,string> is存储<id,名称>
  • 点权存储,使用整型数组int wt[maxn]存储
  • 边权存储,使用二维矩阵int g[maxn][maxn]存储

思路 02

  • map<string,vector<string>> g; //存储边
  • map<string,int> wt; //存储点权
  • map<string,int> vis; //标识顶点是否被访问过
  • map<string,int> res; //存储最终结果,map默认key字典序排序
  • int k,num,sum; //k-判断连通分量总权重是否满足条件的阈值;num-连通分量顶点数;sum-连通分量总权重
  • string head; //满足条件的连通分量点权最大的顶点

2 寻找连通分量,并统计各个连通分量的总边权,总人数,最大点权

思路 01:深度优先遍历DFS

思路 02:广度优先遍历BFS

思路 03:并查集(+路径压缩)

  • 输入测试样例时,将所有关联边进行并查集操作,全部输入完成后,所有连通分量都已划分好并用各自的唯一根id作为连通分量标识
  • 再次遍历所有顶点,并使用并查集的find函数查找其所在连通分量的根节点,记录权重,人数,和最大权重顶点名称

问题 01:若连通分量中有环,BFS和DFS过程中累加边权时,可能会重复记录

并查集-解决方案 01

并查集过程中不做任何处理,最后比较判断时,将阈值2,若连通分量的总边权>2阈值即为犯罪团伙

DFS/BFS-解决方案 01

访问完A点累计AC边权后,将AC边权g[A][C]和CA边权g[C][A]都置为0,再次访问到CA时不再累加

DFS/BFS-解决方案 02

使用二维矩阵int evis[maxn][maxn]记录每条边的访问情况,初始为0,访问后置为1

如图1所示,A->B->C,访问A时累加了A与C边权,访问C时累加了A与C边权

问题 02:各个连通分量的总边权,总人数,最大点权如何记录?

DFS/BFS-解决方案 01

定义三个整型变量,记录每个连通分量总边权,最大点权对应id,连通分量总人数

并查集-解决方案 01

使用三个整型数组,数组的下标对应顶点id,并查集处理时,将总边权和最大点权和集合人数统计到根节点id对应的下标处

并查集-解决方案 02

定义连通分量结构体数组node gang[maxn](下标对应顶点id),结构体中定义总边权,最大点权对应id,连通分量总人数。并查集处理时,将总边权和最大点权和集合人数统计到根节点id对应的下标处

易错点

  1. 最终打印的多个连通分量,需要按照最大点权对应的顶点名称的字典序排序(测试点2,5)

Code

Code 01(邻接表 DFS)

#include <iostream>
#include <vector>
#include <map>
using namespace std;
const int maxn=2010;
map<string,vector<string>> g;
map<string,int> wt;
map<string,int> vis;
map<string,int> res;
int k,num,sum;
string head;
void dfs(string s) {
vis[s]=1;
num++;
sum+=wt[s];
if(wt[s]>wt[head])head=s;
for(int i=0; i<g[s].size(); i++) {
if(vis[g[s][i]]==0)
dfs(g[s][i]);
}
}
void dfs_travel() {
for(auto &it:vis) {
if(it.second==1)continue;
num=sum=0;
head=it.first;
dfs(it.first);
if(num>2&&sum/2>k)
res[head]=num;
}
}
int main(int argc,char * argv[]) {
int n,t;
string a,b;
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
cin>>a>>b>>t;
wt[a]+=t;
wt[b]+=t;
g[a].push_back(b);
g[b].push_back(a);
vis[a]=vis[b]=0;
}
dfs_travel();
printf("%d\n",res.size());
for(map<string,int>::iterator it=res.begin(); it!=res.end(); it++) {
printf("%s %d\n",it->first.c_str(),it->second);
}
return 0;
}

Code 02(邻接矩阵 DFS)

#include <iostream>
#include <map>
using namespace std;
const int maxn = 2010;
map<string,int> si;
map<int,string> is;
map<string,int> ans;
int id,k,wt[maxn],g[maxn][maxn],vis[maxn];//wt点权,g边权
int sti(string s) {
if(si[s]==0) {
si[s]=++id; //从1开始
is[id]=s;
}
return si[s];
}
void dfs(int i,int &head,int &num,int &tv) {
num++;
vis[i]=true;
if(wt[i]>wt[head])head=i;
for(int j=1; j<=id; j++) {
if(g[i][j]==0)continue;
tv+=g[i][j];
g[i][j]=g[j][i]=0;
if(vis[j]==false)
dfs(j,head,num,tv);
}
}
void dfs_travel() {
for(int i=1; i<=id; i++) {
if(vis[i]==true)continue;
int head=i,num=0,tv=0;
dfs(i,head,num,tv);
if(num>2&&tv>k)ans[is[head]]=num;
}
}
int main(int argc,char * argv[]) {
int n,w;
string s1,s2;
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
cin>>s1>>s2>>w;
int id1=sti(s1);
int id2=sti(s2);
wt[id1]+=w;
wt[id2]+=w;
g[id1][id2]+=w;
g[id2][id1]+=w;
}
dfs_travel(); //深度优先遍历
printf("%d\n",ans.size());
for(auto it=ans.begin();it!=ans.end();it++){
printf("%s %d\n",it->first.c_str(),it->second);
}
return 0;
}

Code 03(邻接矩阵 BFS)

#include <iostream>
#include <map>
#include <queue>
const int maxn=2010;
using namespace std;
map<string,int> si;
map<int,string> is;
map<string,int> ans;
int id,k,wt[maxn],g[maxn][maxn],vis[maxn],evis[maxn][maxn];//wt点权,g边权
int sti(string s) {
if(si[s]==0) {
si[s]=++id; //从1开始
is[id]=s;
}
return si[s];
}
void bfs(int now, int &head, int &num, int &tv) {
queue<int> q;
q.push(now);
vis[now]=true;
while(!q.empty()) {
int i = q.front();
q.pop();
num++;
if(wt[i]>wt[head])head=i;
for(int j=1; j<=id; j++) {
if(evis[i][j]==1||g[i][j]==0)continue;
tv+=g[i][j];
evis[i][j]=evis[j][i]=1;
if(vis[j]==false) {
q.push(j);
vis[j]=true;
}
}
}
}
void bfs_travel() {
for(int i=1; i<=id; i++) {
if(vis[i]==true)continue;
int head=i,num=0,tv=0;
bfs(i,head,num,tv);
if(num>2&&tv>k)ans[is[head]]=num;
}
}
int main(int argc,char * argv[]) {
int n,w;
string s1,s2;
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
cin>>s1>>s2>>w;
int id1=sti(s1);
int id2=sti(s2);
wt[id1]+=w;
wt[id2]+=w;
g[id1][id2]+=w;
g[id2][id1]+=w;
}
bfs_travel(); //深度优先遍历
printf("%d\n",ans.size());
for(auto it=ans.begin(); it!=ans.end(); it++) {
printf("%s %d\n",it->first.c_str(),it->second);
}
return 0;
}

Code 04(邻接矩阵 并查集 结构体记录连通分量属性)

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=2010;
map<string,int> si;
map<int,string> is;
int id=1,k,father[maxn],wt[maxn];//wt点权,g边权
struct node { //定义每个连通块的属性
int anum,awt,head; //总人数,总权重,队首整数编号
} gang[maxn];
int sti(string s) {
if(si[s]==0) {
si[s]=id; //从1开始
is[id]=s;
id++;
}
return si[s];
}
/* 并查集 初始化 */
void init() {
for(int i=0; i<maxn; i++) father[i]=i;
}
/* 并查集 查 */
int find(int x) {
int a = x;
while(x!=father[x])
x=father[x];
while(a!=father[a]) {
int temp=a;
a=father[a];
father[temp]=x;
}
return x;
}
/* 并查集 并 */
void Union(int a, int b) {
int x = find(a);
int y = find(b);
if(x<=y)father[y]=x;
else father[x]=y;
}
bool cmp(node &n1,node &n2){
return is[n1.head]<is[n2.head];
}
int main(int argc,char * argv[]) {
init();
int n,w;
string s1,s2;
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
cin>>s1>>s2>>w;
int id1=sti(s1);
int id2=sti(s2);
Union(id1,id2);
wt[id1]+=w;
wt[id2]+=w;
}
for(int i=1; i<id; i++) {
int r = find(i);
gang[r].anum++; //人数增加1
gang[r].awt += wt[i]; //总权重增加
if(wt[gang[r].head]<wt[i]) {
gang[r].head=i;
}
}
sort(gang+1,gang+id,cmp);
vector<int> v;
for(int i=1; i<=id; i++) {
if(gang[i].awt>2*k&&gang[i].anum>2)v.push_back(i);
}
printf("%d\n",v.size());
for(int i=0; i<v.size(); i++) {
printf("%s %d\n",is[gang[v[i]].head].c_str(),gang[v[i]].anum);
}
return 0;
}

Code 05(邻接矩阵 并查集 整型数组记录连通分量属性)

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn=2010;
map<string,int> si;
map<int,string> is;
int id=1,k,father[maxn],wt[maxn],awt[maxn],anum[maxn],head[maxn];//wt点权,g边权
int sti(string s) {
if(si[s]==0) {
si[s]=id; //从1开始
is[id]=s;
id++;
}
return si[s];
}
/* 并查集 初始化 */
void init() {
for(int i=0; i<maxn; i++) father[i]=i;
}
/* 并查集 查 */
int find(int x) {
int a = x;
while(x!=father[x])
x=father[x];
while(a!=father[a]) {
int temp=a;
a=father[a];
father[temp]=x;
}
return x;
}
/* 并查集 并 */
int Union(int a, int b) {
int x = find(a);
int y = find(b);
if(x<=y)father[y]=x;
else father[x]=y;
}
bool cmp(int a, int b) {
return is[head[a]]<is[head[b]];
}
int main(int argc,char * argv[]) {
init();
int n,w;
string s1,s2;
scanf("%d %d",&n,&k);
for(int i=0; i<n; i++) {
cin>>s1>>s2>>w;
int id1=sti(s1);
int id2=sti(s2);
Union(id1,id2);
wt[id1]+=w;
wt[id2]+=w;
}
for(int i=1; i<id; i++) {
int r = find(i);
awt[r]+=wt[i]; //累计权重
anum[r]++; //累计人数
if(wt[head[r]]<wt[i]) {
head[r]=i;
}
}
vector<int> v;
for(int i=1; i<id; i++) {
if(awt[i]>2*k&&anum[i]>2)v.push_back(i);
}
sort(v.begin(),v.end(),cmp); //不排序不符合条件,有两个测试点错误
printf("%d\n",v.size());
for(int i=0; i<v.size(); i++) {
printf("%s %d\n",is[head[v[i]]].c_str(),anum[v[i]]);
}
return 0;
}

PAT Advanced 1034 Head of a Gang (30) [图的遍历,BFS,DFS,并查集]的更多相关文章

  1. pat 甲级 1034. Head of a Gang (30)

    1034. Head of a Gang (30) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue One wa ...

  2. PAT 甲级 1034 Head of a Gang (30 分)(bfs,map,强连通)

    1034 Head of a Gang (30 分)   One way that the police finds the head of a gang is to check people's p ...

  3. PAT Advanced 1053 Path of Equal Weight (30) [树的遍历]

    题目 Given a non-empty tree with root R, and with weight Wi assigned to each tree node Ti. The weight ...

  4. PAT甲级1034. Head of a Gang

    PAT甲级1034. Head of a Gang 题意: 警方找到一个帮派的头的一种方式是检查人民的电话.如果A和B之间有电话,我们说A和B是相关的.关系的权重被定义为两人之间所有电话的总时间长度. ...

  5. PAT 1034. Head of a Gang (30)

    题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1034 此题考查并查集的应用,要熟悉在合并的时候存储信息: #include <iostr ...

  6. PAT甲题题解-1034. Head of a Gang (30)-并查集

    给出n和k接下来n行,每行给出a,b,c,表示a和b之间的关系度,表明他们属于同一个帮派一个帮派由>2个人组成,且总关系度必须大于k.帮派的头目为帮派里关系度最高的人.(注意,这里关系度是看帮派 ...

  7. pat 甲级 1034 ( Head of a Gang )

    1034 Head of a Gang (30 分) One way that the police finds the head of a gang is to check people's pho ...

  8. 1034. Head of a Gang (30) -string离散化 -map应用 -并查集

    题目如下: One way that the police finds the head of a gang is to check people's phone calls. If there is ...

  9. 1034. Head of a Gang (30)

    分析: 考察并查集,注意中间合并时的时间的合并和人数的合并. #include <iostream> #include <stdio.h> #include <algor ...

随机推荐

  1. BusyBox 添加 自定义命令\小程序 (applet)

    背景 在做嵌入式开的时候,busybox无疑是非常好用的命令集,所以很多时候都喜欢把busybox移植到我们的系统里面. 说明 添加一个很简单的命令--hello_busybox,输出"He ...

  2. C语言学习巩固(泛型选择)

    在程序设计中,没有特定类型,但是一旦指定一种类型,就可以转换成指定类型的代码, 例如C++有一个关键tmeplate关键字用于声明模板,而模板的最大应用便是创建泛型算法.可惜的是c没有这个关键字 但是 ...

  3. Day6-T4

    原题目 Describe:差分约束模板题吧...LG上竟然是省选+ code: #include<bits/stdc++.h> #define INF 214748364 using na ...

  4. R box-cox变换 《回归分析与线性统计模型》page100

    > rm(list = ls()) > library(openxlsx) > electric= read.xlsx("data101.xlsx",sheet ...

  5. 剑指offer 按之字型顺序打印二叉树

    题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推.   使用两个栈进行存储,我们在打印某一行节点 ...

  6. 使用python将请求的requests headers参数格式化方法

    import json # 使用三引号将浏览器复制出来的requests headers参数赋值给一个变量 headers = """ Host: zhan.qq.com ...

  7. USB2.0主机控制器 UPD720114 简单详解

    UPD720114 是符合 USB 2.0规格的集线器控制器,适用于“符合生态原则的解决方案”.这种小型封装的控制器集成了核心逻辑电路的2.5 V 内部电压调整器.终端电阻器,减少了所需要的外部组件的 ...

  8. 第九届蓝桥杯省赛c/c++真题明码题解答案,另类excel解法思路

    直到快比赛才重视起之前学校给报了蓝桥杯,且这段时间一直在做Python,所以没做什么准备. 赛场上做这道题时连反码补码的知识点都记混,所以直接用了excel做这道题目,分享下做题思路.及题解. 标题: ...

  9. 114-PHP判断类变量是否相同

    <?php class ren{ //定义人类 } class mao{ //定义人类 } $ren=new ren(); //实例化人类的对象 $ren_a=new ren(); //实例化人 ...

  10. 057-while循环

    <?php $x=1; //初始化变量 while($x<=5){ //执行while循环 echo "$x<br />"; $x++; } ?>