PAT A1034 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.
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
实现思路:
这道题可以采用两种方法去做,一种是单纯将题目看作是一个无向图,用深搜的方式遍历统计,还有一种方法很明显可以看出这是一个并查集题目,自己在做的时候分别想到了2种方法,于是采用了两个方法去实现,深搜方式不说了,更像是暴力搜索,这里解释一下并查集方式,采用并查集首先知道Union合并函数是将有通话记录的双方合并成一个团伙,题目还有一个关键就是要找出团伙中的头目(就是通话总时长最大的那个人),这里正好可以抽样成father[N]数组,团伙就是一个团队里的公共父节点,可以很好地用并查集来实现,个人感觉这是一道好题目,如果题目可以采用多种方式去实现的,这种题目不要放过,多用几个办法去做,可以说是比较经典的图论题了,值得多做。
AC代码:
方法一 DFS法
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
const int MAXN=2021;
map<string,int> n2id;
map<int,string> id2n;
int n,total,G[MAXN][MAXN],cnt=0,toll,hashT[MAXN]= {0};
bool vist[MAXN]= {false};
void dfs(int x,int &weight,int &num,int &ansId) {
if(!vist[x])
num++;//用来统计团伙人数因为会有多次访问同一个结点要用vist数组
vist[x]=true;
for(int i=0; i<cnt; i++) {
if(G[x][i]!=0) {//当这两个人之间有通话时
if(hashT[i]>hashT[ansId]) {//当目前这个人通话时间大于之前团伙中最大的通话时长的时候
ansId=i;
}
weight+=G[x][i];//计算团伙总通话时间
G[x][i]=G[i][x]=0;//计算过的通话时间将2人通话记录清除
dfs(i,weight,num,ansId);
}
}
}
int main() {
string a,b;
fill(G[0],G[0]+MAXN*MAXN,0);
cin>>n>>total;
while(n--) {
cin>>a>>b>>toll;
if(n2id.count(a)==0) {
n2id[a]=cnt;
id2n[cnt]=a;
cnt++;
}
if(n2id.count(b)==0) {
n2id[b]=cnt;
id2n[cnt]=b;
cnt++;
}
G[n2id[a]][n2id[b]]+=toll;
G[n2id[b]][n2id[a]]+=toll;
hashT[n2id[a]]+=toll;//计算个人所有通话时间
hashT[n2id[b]]+=toll;
}
map<string,int> ans;
for(int i=0; i<cnt; i++) {
int weight=0,num=0,ansId=i;
dfs(i,weight,num,ansId);
if(weight>total&&num>2) ans[id2n[ansId]]=num;
}
cout<<ans.size()<<endl;
for(auto it : ans) {
cout<<it.first<<" "<<it.second<<endl;
}
return 0;
}
方法二 并查集
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int MAXN=2020;
const int INF=0x3fffffff;
int total,G[MAXN][MAXN],father[MAXN],allWeigh[MAXN]= {0};//allWeigh统计每个结点的度
unordered_map<string,int> toId;
unordered_map<int,string> toName;
int hashT[MAXN]= {0},pNum[MAXN]= {0}; //pNum统计团伙人数 hashT用于团伙总通话时间的两倍
int findFather(int x) {
int a=x;
while(x!=father[x]) {
x=father[x];
}
while(a!=father[a]) {
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
void Union(int a,int b) {
int faA=findFather(a);
int faB=findFather(b);
if(allWeigh[faA]>allWeigh[faB]) father[faB]=faA;//让通话时间长的人作为团伙的头目
else father[faA]=faB;
}
int main() {
fill(G[0],G[0]+MAXN*MAXN,INF);
for(int i=0; i<MAXN; i++) father[i]=i;
int n,weigh,cnt=0;
cin>>n>>total;
string a,b;
for(int i=0; i<n; i++) {
cin>>a>>b>>weigh;
if(toId.count(a)==0) {
toId[a]=cnt;
toName[cnt]=a;
cnt++;
}
if(toId.count(b)==0) {
toId[b]=cnt;
toName[cnt]=b;
cnt++;
}
G[toId[a]][toId[b]]=G[toId[b]][toId[a]]=weigh;
allWeigh[toId[a]]+=weigh;//累加通话时间
allWeigh[toId[b]]+=weigh;
}
for(int i=0; i<cnt; i++) {
for(int j=i+1; j<cnt; j++) {
if(G[i][j]!=INF) {
Union(i,j);//当2人有通话的时候归入同一团伙
}
}
}
for(int i=0; i<cnt; i++) {
int fid=findFather(i);//找出团伙头目
hashT[fid]+=allWeigh[i];//统计以该团伙头目为首的团队总通话时间
pNum[fid]++;//团伙人数累加
}
map<string,int> ans;
for(int i=0; i<cnt; i++) {
if(pNum[i]>2&&(hashT[i]/2)>total) {
ans[toName[i]]=pNum[i];//当团伙人数>2且通话时间大于门槛时候则是要输出的团伙
}
}
cout<<ans.size()<<endl;
for(auto it : ans) {
cout<<it.first<<" "<<it.second<<endl;
}
return 0;
}

浙公网安备 33010602011771号