题目
警察找到团伙头目的一种方法是检查人们的通话。
如果 A 和 B 之间有通话,我们就说 A 和 B 是相关的。并且关联具有传递性,即如果 A 与 B 关联,B 与 C 关联,那么 A 与 C 也是关联的。
关联权重定义为两人之间所有通话的总时间长度。
一个“帮派”是一个由至少3个相互关联的人组成的群体,并且其总关联权重大于给定的阈值 K。
在每个帮派中,总权重最大的就是头目,数据保证每个帮派中总权重最大的人是唯一的。
你需要确定各个帮派以及帮派头目。
输入格式
第一行包含两个整数 N 和 K,表示通话数量以及权重阈值。
接下来 N 行,每行采用如下形式描述通话:
Name1 Name2 Time
Name1 和 Name2 是通话的两人的名字,Time 是通话时间。
名字的长度固定为 3,且只包含大写字母。
时间是一个不超过 1000 的正整数。
输出格式
第一行输出帮派数量。
接下来每行输出一个帮派的信息,包括帮派头目名字以及帮派人数。
帮派信息按头目名字字典序输出。
数据范围
1≤N,K≤1000
输入样例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
输出样例1:
2
AAA 3
GGG 3
输入样例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
输出样例2:
0
思路
本题有两种解法,一种并查集,一种深度遍历。下面代码属于并查集版本。并查集的特点在于所有属于一个团伙的节点的头节点为同一个。只需比较头节点即可确定两个人是否属于同一个帮派,以此来找出老大,以及成员数量。
代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Scanner;
class Main{
//建立成员姓名和编号之间的映射关系,将成员转化为节点,便于建模
static HashMap<String, Integer>M1=new HashMap<>();
static HashMap<Integer, String>M2=new HashMap<>();
//建立并查集,最多不超过2000个节点
static boolean []visited=new boolean [2001];
static int [] p=new int [2001];//该节点的头结点编号
static int [] cnt=new int [2001];//头节点记录所有成员人数
static {//初始化并查集,头节点初始为自己,成员数为1
for(int i=0;i<2001;i++) {
p[i]=i;
cnt[i]=1;
}
}
static int time[]=new int [2001];//存放各人的通话时长
static int find(int i) {//返回值为该集合的头节点
if(p[i]!=i) p[i]=find(p[i]);//直到找到父节点为自身的节点停止,同时将父节点设置为头节点
return p[i];
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int k=sc.nextInt();
int index=0;//节点编号(节点个数)
for(int i=0;i<n;i++) {
String a=sc.next();
String b=sc.next();
int t=sc.nextInt();
if(!M1.containsKey(a)) {//如果是新的成员,则建立一个新的映射
M1.put(a,index);
M2.put(index, a);
index++;
}
if(!M1.containsKey(b)) {//如果是新的成员,则建立一个新的映射
M1.put(b,index);
M2.put(index, b);
index++;
}
time[M1.get(a)]+=t;//个人通话时间增加
time[M1.get(b)]+=t;//个人通话时间增加
int fa=find(M1.get(a));
int fb=find(M1.get(b));
if(fa!=fb) {//如果啊a,b不在同一个集合内,则合并集合
p[fa]=fb;
cnt[fb]+=cnt[fa];
}
}
//此时各集合已经建立完毕,再来确定帮派是否成立,以及帮派的首领
ArrayList<String> boos=new ArrayList<>();//用来存放帮派头目姓名
for(int i=0;i<M1.size();i++) {//遍历所有节点
int head=find(i);
if(cnt[head]>=3&&(!visited[i])) {//如果帮派人数
visited[i]=true;
int boos_time=time[i];//boos的通话时长
int totaltime=time[i];//帮派的总通话时长
int boos_id=i;//boos的节点编号
for(int j=0;j<M1.size();j++) {
if(find(j)==head&&(!visited[j])) {//如果属于同一个帮派且未被访问
visited[j]=true;
totaltime+=time[j];
if(time[j]>boos_time) {
boos_time=time[j];
boos_id=j;
}
}
}
if(totaltime>k*2) boos.add(M2.get(boos_id));//如果总通话时长大于两倍的阈值,则帮派成立
}
}
Collections.sort(boos);//将boos按照姓名升序排列
System.out.println(boos.size());
for(int i=0;i<boos.size();i++) {
System.out.println(boos.get(i)+" "+cnt[find(M1.get(boos.get(i)))]);
}
}
}
总结
测试点5超时,搜索原因在于一个团伙内的首领确定问题,两个人有相同的权值,需要按照名字来确定首领。