原题目: 1034 Head of a Gang (30 分).
题意
警察通过犯罪团伙成员间的通话时长来判断头目。
一个犯罪团伙至少有3人,彼此间的总权值(通话时长)大于给定阈值K。每个团伙中,总权值最高的为头目。
给出通话次数N和权值阈值K(均小于等于1000);接着N行,给出通话双方姓名(三个大写字母组成)以及通话时长(不大于1000min的正整数)
① 计算团伙数量,
② 以及每个团伙的头目姓名和成员总数(按头目姓名的字母序排序)。
分析
- 题意分析:一个团伙只有一个头目,犯罪分子不会同时在多个团伙中——图的问题;且是判断无向图的连通分量个数的问题,用DFS遍历;也可以用 并查集 解决(总是保持结点权值更大的结点为集合的根结点)。
- 图的存储:顶点数较少,可以用邻接矩阵存储——顶点为人,矩阵元素为每条边的权值
——① ⭐⭐为了能利用静态数组的随机访问性,将人名转换成数字ID,存储在 两个map 中——一个按名字查找ID,一个按ID查找名字;ID从1开始,用idNumber计数,顺便统计图的总顶点数,便于后面遍历;② 边的权值用于计算一个团伙的总权值totalw,最后需要判断是否大于阈值K(构成“团伙”的要求之以)。 - 寻找头目:设置数组weight,计算顶点权值。
- 深度优先搜索法遍历图:
① 设置数组visit标记访问情况。
② 写一个遍历图的函数DFSTrave(),枚举所有连通分量,调用遍历连通分量中所有顶点的函数DFS,然后将符合团伙(人数>2且总权值>k)的记入map中(再开一个map用于记录答案团伙,用到按值查找名字,键为名字,值为团伙人数)。
③ 再写一个遍历连通分量中所有顶点的函数DFS——① 通过 引用 返回各连通分量的头目、人数、总权值;② 特别地,这题除了顶点以外还需要 遍历所有边,为了遍历到且不重复,故遍历过的边的权值设为0。
知识点
- 计算无向图的连通分量个数——深度优先搜素法DFS遍历图;且需要遍历所有边——遍历过的边权值设为0。
- 写函数给字符串编号,使字符串可以随机访问数组;并用map记录字符串与编号的对应关系(可以设置两个——一个按编号查,一个按字符串查)。
词汇
WORDS | 释义 | WORDS | 释义 |
---|---|---|---|
gang | 一帮,一伙(罪犯) | threshold | 门槛;阈值 |
CODE
#include <iostream>
#include <map>
using namespace std;
map<string, int> strToInt; //把名字换成ID(从1开始)(按名字搜索ID)
map<int, string> intToStr; //存储ID对应名字(按ID搜索名字)
map<string, int> ans; //存头目(按名字搜索)
int idNumber = 1; //总人数;生成ID
int k; //阈值
int G[2010][2010], weight[2010]; //图的邻接矩阵;顶点总权值
bool visit[2010]; //标记访问情况
int stoifunc(string s); //返回名字对应ID
void DFS(int u, int &head, int &numMember, int &totalw); //访问顶点u及其所在连通分量
void DFSTrave(); //遍历所有连通分量
int main()
{
int n, w;
cin >> n >> k;
string s1, s2;
for ( int i=0; i<n; i++ ){
cin >> s1 >> s2 >> w;
//获取名字对应ID
int id1 = stoifunc(s1);
int id2 = stoifunc(s2);
//累计个人总权值
weight[id1] += w;
weight[id2] += w;
//生成邻接矩阵
G[id1][id2] += w;
G[id2][id1] += w;
}
//遍历图
DFSTrave();
//输出
cout << ans.size() << endl;
for ( auto it=ans.begin(); it!=ans.end(); it++ )
cout << it->first << " " << it->second << endl;
return 0;
}
int stoifunc(string s){
if ( strToInt[s]==0 ){ //没转换过的名字
strToInt[s] = idNumber;
intToStr[idNumber] = s;
return idNumber++; //返回名字对应ID
}
else
return strToInt[s]; //转换过了直接返回ID
}
void DFS(int u, int &head, int &numMember, int &totalw){ //统计该连通分量的头目、人数、头目总权值
visit[u] = true; //标记访问
numMember++;
if ( weight[u]>weight[head] )
head = u; //顶点总权值大的为头目
for ( int v=1; v<idNumber; v++ ){
if ( G[u][v]>0 ){
totalw += G[u][v];
G[u][v] = G[v][u] = 0; //遍历过一条边后就设该边权值为0(防止回路遍历)
if ( visit[v]==false )
DFS(v, head, numMember, totalw);
}
}
}
void DFSTrave(){
for ( int i=1; i<idNumber; i++ ){
if ( visit[i]==false ){ //遍历顶点i及其所处连通分量
int head = i, numMember = 0, totalw = 0;
DFS(i, head, numMember, totalw);
if ( numMember>2 && totalw>k )
ans[intToStr[head]] = numMember; //需要符合构成团伙的要求
}
}
}