1034 Head of a Gang (30point(s))

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

题目大意:给出了一组通话记录构成一个图,我们需要获得图包含连通分量的个数,以及每个连通分量包含的总边权值、节点个数、最大权值节点编号。若某连通分量符合犯罪团伙定义,则对应输出犯罪头目和团伙人数。

分析:属于图的遍历,可采用并查集 / DFS。

两种方法共同处:

输入是以字符串的形式给出的,且结果需要输出字符串,为方便图的遍历,这里需要建立映射关系。

1)  map<string,int>in;(从0开始映射,顺便统计节点(人)数)。
2)  map<int,string>str;
3)  map<string,int>gang; //记录犯罪团伙用于输出。

一:并查集做法:


1)由于通话记录是一条条读入的,只有所有的记录读入完成,节点的点权和边的边权值(累加处理)才能最终确定,因此这里采用输入完成后for循环对边的两端端点进行Union处理。(两个人可能有多条通话记录,采用map标记,防止重复累加)。

2)合并时选择点权最大的节点作为根(头目),root[ ]数组用于记录节点个数。W[ r ]用于 r 头目所在团伙的总边权。在find()根过程不进行路径压缩处理(路径压缩会改变图的联通情况,会导致边权紊) 。再者,在并查集处理过程中若a,b节点所在集合的根节点相同,是无法保存e[a][b]边权的,进行如下处理。

按1,2,3顺序读入边权进行并查集操作,由于F和H的根相同是G,因此将(F,H)边权累加至1,形成右边所示结构。

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2010; 
map<string,int>in;
map<int,string>str;
map<string,int>gang;//记录团伙数 
int weight[maxn],num=0;//分别记录点权和总人数;
int e[maxn][maxn];
int change(string s){
	if(in.find(s)!=in.end()){
		return in[s];
	}else{
		in[s]=num;
		str[num]=s;
		return num++;
	}
}
int father[maxn],root[maxn],W[maxn];
int find(int x){
	while(x!=father[x]) x=father[x];
	return x; 
}
void Union(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){ //让点权最大的头目成为根 
		weight[fx]>weight[fy]? father[fy]=fx:father[fx]=fy;
	}else e[father[x]][x]+=e[x][y];//对回路进行处理,注意这里并没有判断father【x】==x?,也就是说可能会处理出自回路。
} 
struct node{
	int u,v;
};
int main(){
	int n,k,w;
	cin>>n>>k;
	vector<node>vt;
	for(int i=0;i<maxn;i++) father[i]=i;//初始化每个节点为根。 
	for(int i=0;i<n;i++){
		string s1,s2;
		cin>>s1>>s2>>w;
		int u=change(s1);
		int v=change(s2);
		weight[u]+=w;
		weight[v]+=w;
		e[u][v]+=w; //存储无向边
		e[v][u]+=w;
		vt.push_back({u,v});
	}
	unordered_map<int,bool>mp; //用 int数组标记会出现一个段错误  
	for(int i=0;i<vt.size();i++){
		if(mp[vt[i].u*1000+vt[i].v]) continue;
		Union(vt[i].u,vt[i].v);//合并下来的连通图是没有回路的
		mp[vt[i].u*1000+vt[i].v]=true;
		mp[vt[i].v*1000+vt[i].u]=true;
	}
	for(int i=0;i<num;i++){ //遍历整个图 
	    int r=find(i),f=father[i];
		root[r]++;  //团伙人数 
//		if(r!=i){ // 加上这个判断条件 case 2 五分测试点过不了,这里是因为Union时
                //若两个待合并节点a,b的根ruo相同, 要进行回路处理。
		    W[r]+=e[f][i];//团伙的总通话时长。
//	    }
	}
    for(int i=0;i<num;i++){
    	if(root[i]>2 && W[i]>k )
    	    gang[str[i]]=root[i];
	}
	cout<<gang.size()<<endl;
	for(auto it:gang){
		cout<<it.first<<" "<<it.second<<endl;
	}
	return 0;
}

凡是能用并查集做的都可以使用DFS深搜做(当然不排除超时可能),下面看DFS解法。

二:DFS做法:

图的深搜遍历属于基操了,需要注意的是:遍历过程需要得到连通分量的头目head、节点总数people、总权值totalval,因此进行引用传参。

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2010; 
map<string,int>in;
map<int,string>str;
map<string,int>gang;//记录团伙数 
int weight[maxn],num=0//分别记录点权和总人数;
int e[maxn][maxn];
bool vis[maxn];
int change(string s){
	if(in.find(s)!=in.end()){
		return in[s];
	}else{
		in[s]=num;
		str[num]=s;
		return num++;
	}
}
//遍历某个连通分量 
void dfs(int visid,int &head,int &people,int &totalweight){
	people++;
	vis[visid]=true;
	if(weight[visid]>weight[head]) head=visid;
	for(int i=0;i<num;i++){
		if(e[visid][i]){		
			totalweight+=e[visid][i];
			// e[visid][i]=e[i][visid]=0,若存储的是无向边,得加上这句代码,
			//否则将会出现回路重复遍历情况,比如先遍历1->2,再遍历 2->1。 
			if(vis[i]==false)
			    dfs(i,head,people,totalweight);
		}
	}
}
int main(){
	int n,k,w;
	cin>>n>>k;
	for(int i=0;i<n;i++){
		string s1,s2;
		cin>>s1>>s2>>w;
		int u=change(s1);
		int v=change(s2);
		weight[u]+=w;
		weight[v]+=w;
		e[u][v]+=w; //存储有向边 
	}
	for(int i=0;i<num;i++){ //遍历整个图 
		if(vis[i]==false){
			int head=i,people=0,totalweight=0;
			dfs(i,head,people,totalweight);
			if(people>2 && totalweight>k){
				gang[str[head]]=people;
			}
		}
	}
	cout<<gang.size()<<endl;
	for(auto it:gang){
		cout<<it.first<<" "<<it.second<<endl;
	}
	return 0;
}

显然,DFS深搜简单点。

End!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZCAIHUI_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值