1034 Head of a Gang PAT甲级 Java

题目

警察找到团伙头目的一种方法是检查人们的通话。

如果 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超时,搜索原因在于一个团伙内的首领确定问题,两个人有相同的权值,需要按照名字来确定首领。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值