一、题目大意
- PAT A1034
- 给出若干人之间的通话记录,这些通话将他们分成若干组。每个组的总边权值为该组内所有通话的时间之和,每个人的点权为该人参与的通话时间之和。现在只要某个组的总边权值大于一个阈值 k,并且组内人数大于 2,就将该组视为“犯罪团伙(gang)”,该组内点权最大的人视为头目。要求输出“犯罪团伙”的数目,并按头目姓名字典序的方式输出每个“犯罪团伙”的头目姓名和人数。
二、解题思路
- 首先解决姓名与编号的对应关系,编号与姓名的对应关系。具体用 map 实现。
- 需要获得每个人的点权,即与之相关的通话记录的时长之和,这个在读入数据时就可以处理,用 W[maxn] 来记录每个人的点权。
- 进行图的遍历。使用 DFS 遍历每个连通块,获取每个连通块的头目,成员个数,总边权值。进行判断,并将结果存储。
三、参考代码
#include<iostream>
#include<map>
#include<string>
using namespace std;
const int maxn = 2010;
int num_person = 0, k;
int W[maxn] = { 0 }, G[maxn][maxn] = { 0 };
bool vis[maxn] = { false };
map <string, int> string_int;
map <int, string> int_string;
map <string, int> gang;
int change(string s) {
if (string_int.find(s) != string_int.end())
return string_int[s];
else {
string_int[s] = num_person;
int_string[num_person] = s;
return num_person++;
}
}
void DFS(int v, int &head, int &num_member, int &total_value) {
vis[v] = true;
num_member++;
if (W[v] > W[head]) head = v;
for (int i = 0; i < num_person; i++) {
if (G[v][i]) {
total_value += G[v][i];
G[v][i] = G[i][v] = 0;
if (!vis[i])
DFS(i, head, num_member, total_value);
}
}
}
void DFS_trave() {
for (int i = 0; i < num_person; i++) {
if (!vis[i]) {
int head = i, num_member = 0, total_value = 0;
DFS(i, head, num_member, total_value);
if (num_member > 2 && total_value > k)
gang[int_string[head]] = num_member;
}
}
}
int main() {
int n, t;
cin >> n >> k;
for (int i = 0; i < n; i++) {
string s1, s2;
cin >> s1 >> s2 >> t;
int n1 = change(s1);
int n2 = change(s2);
W[n1] += t;
W[n2] += t;
G[n1][n2] += t;
G[n2][n1] += t;
}
DFS_trave();
cout << gang.size() << endl;
for (auto it = gang.begin(); it != gang.end(); it++)
cout << it->first << ' ' << it->second << endl;
return 0;
}
四、解题感悟
- 本题也可以采用并查集解决。以后有时间再试试。
- 本题一开始觉得挺复杂的,照着书上的代码写了一遍,然后自己又重新写了一遍,才感觉理顺了一些。代码中关于图中有环的情况的处理值得借鉴。