题目
题目大意
一个犯罪团伙满足条件:人数 > 2;团伙内的总通话时长 > k。团伙首领就是该团伙中通话时长最多的。先给定一组通话,格式为 A B time,要求输出犯罪团伙的数目,并输出每个团伙的首领名字和该团伙的人数。如果没有犯罪团伙,就输出0。
思路
大致的思路并不难,但是有许多细节的坑点。求犯罪团伙的数量就是求图的连通分量,确定了这一点后,再考虑图的存储。
该题和别的题不同,不是给下标,而是给名字,并且还有权值,就不能用二维string数组了,只能用邻接表,但邻接表又对应下标。因此需要建立一个从名字到下标的映射,就用map了,既方便图的构建,又方便后续由下标找名字的操作(当然,也可以再建一个从下标到名字的map,后面找名字会更加方便,但是我有点懒没有弄hhh)。
构建图还需要注意一点,就是A给B通话20,B给A通话10,那么A到B和B到A的权值都是30。(这点我调试的时候才发现)
求连通分量的数目用dfs,很经典了。刚开始我在里面加了计算每个连通分量总通话时长的代码,但由于连通分量有可能是环,用dfs会少加一条边,所以就把计算total挪到dfs后面了。找到连通分量后再算这个分量的总权值,为防止重复,加过一条边就把这条边的权值设为0。
找到犯罪团伙的首领后,还需要按照人名的字典序输出,否则测试点2、5错误。所以我又加了个map,map可以自动排序。
测试点3段错误是因为开的数组太小了,无向图最好开到2 * n + 1。
代码
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int n, m; // 这里我把k换成m了
int g[2001][2001]; // 刚开始开的1001,测试点3段错误,无向图最好开到2*n + 1
map<string, int> mp; // 名字-下标
map<int, int> res; // 存储各个连通分量的犯罪首领及团伙人数
bool b[2001] = {false};
vector<int> team; // 记录每个连通分量的成员
int total = 0; // 连通分量的总权值
void dfs(int vi){
b[vi] = true;
team.push_back(vi);
for (int i = 1; i <= n; i++){
if (!b[i] && g[vi][i]){
dfs(i);
}
}
}
int main(){
cin >> n >> m;
int temp = 1; // 记录名字的下标
for (int i = 0; i < n; i++){
string v1, v2;
int time;
cin >> v1 >> v2 >> time;
if (i == 0){
mp[v1] = temp;
temp++;
mp[v2] = temp;
temp++;
}else{
int flag1 = 0, flag2 = 0; // 标记有没有在mp中找到v1,v2
for (auto it = mp.begin(); it != mp.end(); it++){
if (v1 == it->first){
flag1 = 1;
}
if (v2 == it->first){
flag2 = 1;
}
}
if (!flag1){
mp[v1] = temp;
temp++;
}
if (!flag2){
mp[v2] = temp;
temp++;
}
}
if (g[mp[v1]][mp[v2]]){
g[mp[v1]][mp[v2]] += time;
g[mp[v2]][mp[v1]] += time;
}else{
g[mp[v1]][mp[v2]] = g[mp[v2]][mp[v1]] = time;
}
} // 构建图
for (int i = 1; i <= n; i++){
if (!b[i]){
team.clear();
total = 0;
dfs(i);
if ((int)team.size() > 2){
for (int j = 0; j < (int)team.size(); j++){
for (int k = 0; k != j && k < (int)team.size(); k++){
total += g[team[j]][team[k]];
g[team[j]][team[k]] = 0; // 加过的边直接设为0
}
}
} // 如果是环,total无法在dfs中计算,所以要另外计算total
if ((int)team.size() > 2 && total > m){
int maxTime = 0, head;
for (int j = 0; j < (int)team.size(); j++){
int sum = 0;
for (int k = 1; k <= n; k++){
if (g[team[j]][k]){
sum += g[team[j]][k];
}
if (g[k][team[j]]){
sum += g[k][team[j]];
}
}
if (maxTime < sum){
maxTime = sum;
head = team[j];
}
} // 找犯罪团伙的首领
res[head] = (int)team.size();
}
}
}
if (res.size() == 0){
cout << "0" << endl;
}else{
cout << res.size() << endl;
map<string, int> result; // 题目要求犯罪首领输出按字典序(测试点2、5)
for (auto i = res.begin(); i != res.end(); i++){
for (auto j = mp.begin(); j != mp.end(); j++){
if (i->first == j->second){
result[j->first] = i->second;
break;
}
}
}
for (auto it = result.begin(); it != result.end(); it++){
cout << it->first << " " << it->second << endl;
}
}
return 0;
}