PAT甲级 1034 Head of a Gang(30) (并查集)

题目

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.

输入

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.

输出

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.

样例输入 

样例输入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

样例输入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

样例输出 

样例输出1:

2
AAA 3
GGG 3

样例输出2:

0

题意理解

这题让你找到有多少个符合条件的帮派(gang)

输入n条通话信息 

每条通话信息有 成员名字1 成员名字2 通话时间

那么什么是符合条件的帮派呢

1.帮派成员人数必须严格大于2

2.帮派成员所有通话时间必须严格大于k

符合条件的帮派里面 我们输出帮派里面通话时间最长的那个人 和帮派人数

注意输出的人名要按照字典序排序

这题我们首先想到的就是并查集处理

把所有的名字都通过哈希映射成id 然后通过并查集维护这个帮派,也就是这个联通块的人数

维护好所有的帮派以后,通过vector存以这个人为帮派首领的帮派所有人的编号

比如vector<int>gangs[5]  就是根节点为5的帮派所有人 但是这个帮派里面最长通话记录可能并不是这个根节点 那么我们就要重新在里面寻找,找到帮派里面通话时间最长的那个人

找到了以后我们再判断一下,

帮派所有人的通话时间有没有超过K,为什么这里要sum_time/=2;呢

因为我们之前存的是所有人自己的聊天时间 比如A->B聊了30分钟 同样B->A也是聊了30分钟,把帮派里面所有人的聊天时间全部加起来的话相当于加了A->B和B->A两条边 那么我们就要除以2才是帮会所有人的通话时间。

最后记得检查一下有没有字典序排序最后的结果

代码 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,k;
int ae[N],siz[N],ti[N];
unordered_map<string,int> ma;//名字->编号 
unordered_map<int,string> ID;//编号->名字 
vector<string>ans; //最后答案 也就是帮会中通话时间最长的那个人 
vector<int>gangs[N];//每个帮派的集合 
void init(){
    for(int i=1;i<=2*n;i++){
        ae[i]=i;
        siz[i]=1;
    }
}
int find(int x){
   if(ae[x]!=x)ae[x]=find(ae[x]);
   return ae[x];
}
void merge(int a,int b){
   a=find(a),b=find(b);
   if(a!=b){
   	   siz[b]+=siz[a];
       ae[a]=b;
   }
}
int main(){
    scanf("%d%d",&n,&k);
    int cnt=0,nu;
    string a,b;
    init();
    for(int i=1;i<=n;i++){
        cin>>a>>b>>nu;
        if(!ma.count(a))ma[a]=++cnt;
        if(!ma.count(b))ma[b]=++cnt;
        int x=ma[a],y=ma[b];
        merge(x,y);
        ti[x]+=nu;ti[y]+=nu;
        ID[x]=a;ID[y]=b;
    }
    //所有人数大于2的团队 
    map<int,int>group; 
    for(int i=1;i<=cnt;i++){
    	int root=find(i);
    	if(siz[root]>2){
    	   group[root]=1;
    	   gangs[root].push_back(i);
		}
	}
	//在所有人数大于2的团队中 找到最大通话时间的人 并且团队总通话时间超过k 
	//满足的话加入ans
	for(auto it=group.begin();it!=group.end();it++){
		int root=it->first;
		int time=0,id_member;
		int sum_time=0;
		for(int i=0;i<gangs[root].size();i++){
			int j=gangs[root][i];
			if(ti[j]>time){
				time=ti[j];
				id_member=j;
			}
			sum_time+=ti[j];
		}
		sum_time/=2;
		if(sum_time>k){
			ans.push_back(ID[id_member]);
		}
	} 
	
	sort(ans.begin(),ans.end());
	
    printf("%d\n",ans.size());
    
    for(int i=0;i<ans.size();i++){
    	int en=ma[ans[i]];
    	en=find(en);
    	printf("%s %d\n",ans[i].c_str(),siz[en]);
	}  
	
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值