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 threthold 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.
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
AAA 3
GGG 3
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
0
给如若干通信记录,如果一组人(大于2)之间的通信市场大于K,则定义为一个团伙,该团伙中与其他人通话最多的为团伙的头目。求每个团伙的头目名称和团队人数(按团伙的姓名字典排序)。
使用并查集将有通信记录的人合并起来,合并的同时合并并累加k值。再使用一个一维数组存储每个人涉及的总通信时间。通过并查集合并后将每个人存入对应根结点的集合(根结点不一定是头目)。最后筛选出符合条件的集合,每个集合遍历自身的成员找出头目。再将集合按照头目的名称字典排序即可。
题目本身不难,只是涉及的转换和排序,增加了一些步骤。基本核心代码就是一个并查集,并且在并查集里统计k值。
#include
#include
#include
#include
#include
using namespace std;
#define MAX_N 2000
int parent[MAX_N], r[MAX_N], k[MAX_N], w[MAX_N];
void init(int N){
for(int i = 0; i < MAX_N; i++){
parent[i] = i;
w[i] = 0;
r[i] = 0;
k[i] = 0;
}
}
int find(int p){
while (p != parent[p]) {
parent[p] = parent[parent[p]]; // 路径压缩
p = parent[p];
}
return p;
}
bool connected(int p, int q){
return find(p) == find(q);
}
// union是保留关键字,所以函数名不能为union
void Union(int p, int q, int t){
int rootP = find(p), rootQ = find(q);
if(rootP == rootQ){
k[rootP] += t;
return;
}
// 总是将小树合并到大树上,如果两棵树高度一样,则任意合并,合并后树的高度加1,合并树的同时合并计数
if(r[rootP] < r[rootQ]){
parent[rootP] = rootQ;
k[rootQ] += k[rootP] + t; // 合并并累加k
}else if(r[rootP] > r[rootQ]){
parent[rootQ] = rootP;
k[rootP] += k[rootQ] + t; // 合并并累加k
}else{
parent[rootQ] = rootP;
k[rootP] += k[rootQ] + t; // 合并并累加k
r[rootP]++;
}
}
int population = 0;
map<string, int> indexs;
string names[MAX_N * 2];
// 获得下标
int getIndex(string s){
if(indexs.find(s) == indexs.cend()){
indexs[s] = population;
names[population] = s;
return population++;
}
return indexs[s];
}
bool cmp(int h1, int h2){
return names[h1] < names[h2];
}
vector<int> gangs[MAX_N];
int head[MAX_N], cnt = 0;
int main() {
int N, K;
// 读取输入
scanf("%d %d", &N, &K);
init(N);
string s1, s2;
for(int i = 0, u, v, t; i < N; i++){
cin >> s1 >> s2;
scanf("%d", &t);
u = getIndex(s1);
v = getIndex(s2);
Union(u, v, t);
w[u] += t;
w[v] += t;
}
// 将每个人加入到自己的组中
for(int i = 0; i < population; i++){
// 这里不能直接使用parent[i],因为可能存在需要路径压缩
gangs[find(i)].push_back(i);
}
for(int i = 0, size, h; i < population; i++){
// 过滤k值和人数不符合的组
if(k[i] <= K || (size = (int)gangs[i].size()) <= 2) continue;
// 遍历组内成员获得头目
h = i;
for(int p : gangs[i]){
if(w[p] > w[h]){
h = p;
}
}
head[cnt++] = h;
}
// 对头目按字典名排序
sort(head, head + cnt, cmp);
// 输出结果
printf("%d\n", cnt);
for(int i = 0, p; i < cnt; i++){
p = head[i];
// 但是这里又可以直接使用parent[p],因为在前面加入组的时候对每个点都执行了一次find(p),全部被路径压缩
printf("%s %d\n", names[head[i]].c_str(), (int)gangs[parent[p]].size());
}
return 0;
}