每日一练团伙头目 (25 分)

122 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。​​

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

如果 A 和 B 之间有通话,我们就说 A 和 B 是相关的。并且关联具有传递性,即如果 A 与 B 关联,B 与 C 关联,那么 A 与 C 也是关联的。

关联权重定义为两人之间所有通话的总时间长度。

一个“帮派”是一个由至少3个相互关联的人组成的群体,并且其总关联权重大于给定的阈值 K。

在每个帮派中,总权重最大的就是头目,数据保证每个帮派中总权重最大的人是唯一的。

你需要确定各个帮派以及帮派头目。

数据范围

1≤N,K≤1000

输入格式:

第一行包含两个整数 N 和 K,表示通话数量以及权重阈值。

接下来 N 行,每行采用如下形式描述通话:

Name1 Name2 Time

Name1 和 Name2 是通话的两人的名字,Time 是通话时间。

名字的长度固定为 3,且只包含大写字母。

时间是一个不超过 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
HHH FFF 10

输出样例2:

0

分析

题意

各个名字之间相互通话,相当于名字与名字之间两两有联系,将各个两两有联系的名字用链子串起来组成一个集合,求集合的个数,和各个集合中名字通话的最长时间。

思路

  1. 根据题意分析,可以想到用并查集。
  2. 可以直接将时间大的作为父亲结点,这样只需要寻找每个集合的根节点就能找到大姐大了。
  3. 由于测试数据的不同,会出现两个集合的情况,但这两个集合实际上属于一个团伙,所以得注意合并的问题题 代码如下:
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <set>

using namespace std;

const int N = 2010;

struct Edge
{
    int a, b, c;
}edges[N];

int n, k, idx;
string names[N];
int t[N], f[N], cnt[N], w[N];
unordered_map<string, int> id;

void init()//并查集的初始化 
{
    for(int i = 0; i < N; i++) {
        f[i] = i;
        cnt[i] = 1;
    }
}

int get(string name)
{
    if(id.count(name)) {//被查找元素的个数为>0; 
        return id[name];
    }
    
    id[name] = idx;
    names[idx] = name;//将姓名转化为数字 
    return idx++;
}

int find(int x)
{
    return f[x] = f[x] == x ? x : find(f[x]);//并查集的查找。 
}

void merge(int a, int b, int c)
{
    int fa = find(a), fb = find(b);//找到两个名字的根节点 
    
    if(w[fa] < w[fb]) {
        swap(fa, fb);//将时间大的放前面(让老大当上根节点)
    }
    
    f[fb] = fa;
    
    if(fa != fb) {//根节点不同合并并查集 
        t[fa] += t[fb];
        cnt[fa] += cnt[fb];
    }
    
    t[fa] += c;//记录时间 
}

int main()
{
    init();
    cin >> n >> k;
    for (int i = 0; i < n; i ++ )
    {
        string s1, s2;
        int time;
        cin >> s1 >> s2 >> time;
        
        int a = get(s1), b = get(s2);
        w[a] += time, w[b] += time;//记录每个名字的时间 
        edges[i] = {a, b, time};//放入结构体 
    }
    
    for (int i = 0; i < n; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, c = edges[i].c;
        merge(a, b, c);
    }
    
    set<pair<string, int>> res;
    for(int i = 0; i < idx; i++)
    {
        if(find(i) == i && t[i] > k && cnt[i] >= 3) {//如果可以满足条件 
            res.insert({names[i], cnt[i]});//大姐大名字和相应的人数放进set中。 
        }
    }
    
    cout << res.size() << endl;
    for(auto x : res) {
        cout << x.first << ' ' << x.second << endl;
    }
    return 0;
}