原题链接:1034 Head of a Gang (30分)
关键词:dfs、并查集
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 threshold 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.
Input Specification:
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.
Output Specification:
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.
Sample Input 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
Sample Output 1:
2
AAA 3
GGG 3
Sample Input 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
Sample Output 2:
0
题目大意:
帮派:成员大于等于两人,所有成员的通话时间超过规定值k;
头目:一个帮派中权值最大的人;
现在给出通话记录,请你找出所有帮派(输出头目名称)以及帮派里的人数。
注意:
- 有1000条通话记录,那么最极端的情况下有2000个人打电话,maxn设置为1010会产生段错误;
- 选择头目的时候,需要对头目按照字典序排序。可以用vector存然后sort,也可以用map记录头目名称和团队人数,map会自动排序;
1、并查集做法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2000;
int parent[maxn], r[maxn], k[maxn], w[maxn];
void init(int N){
for(int i = 0; i < maxn; 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[maxn * 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[maxn];
int head[maxn], 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;
}
2、DFS做法:
// pat 1034
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2010;
int n, k, num = 0; //人数 阈值 人的编号(总人数)
int g[maxn][maxn] = {0}, weight[maxn] = {0}; //weight 点权
bool vis[maxn] = {false}; //标记是否访问
map<string, int> mp1; //姓名 <-> 编号
map<int, string> mp2; //编号 <-> 姓名
map<string, int> gang; //头目 <-> 人数
int getid(string s){ //获得姓名对应的编号
if(mp1.find(s) != mp1.end()) { //已经出现过
return mp1[s];
}
else{
mp1[s] = num;
mp2[num] = s;
return num++;
}
}
//一次dfs遍历一个连通块 ,一定要有引用
void dfs(int nowvisit, int &head, int &numMumber, int &sumW){ //当前访问 头目 成员编号 总权值
numMumber++; //成员数+1
vis[nowvisit] = true;
if(weight[nowvisit] > weight[head]) head = nowvisit; //权值比头目大 更新
for(int i = 0; i < num; i ++ ){
if(g[nowvisit][i] > 0){
sumW += g[nowvisit][i]; //连通块总权值加上这条边
g[nowvisit][i] = g[i][nowvisit] = 0; //删除这条边,防止回头
if(!vis[i]){
dfs(i, head, numMumber, sumW);
}
}
}
}
int main(){
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
cin >> n >> k;
for(int i = 0; i < n; i ++ ){
string s1, s2;
int w;
cin >> s1 >> s2 >> w;
int a = getid(s1);
int b = getid(s2);
g[a][b] += w; //此处不能用g[a][b] = g[b][a] = w; 因为可能不止通话一次
g[b][a] += w;
weight[a] += w;
weight[b] += w;
}
//遍历所有连通块
for(int i = 0; i < num; i++){
if(!vis[i]){
int head = i, numMumber = 0, sumW = 0;
dfs(i, head, numMumber, sumW);
if(numMumber > 2 && sumW > k){
gang[mp2[head]] = numMumber; //map可以自动按照key值排序
}
}
}
//输出
cout << gang.size() << endl;
map<string, int>::iterator it; //迭代器
for(it = gang.begin(); it != gang.end(); it++){
cout << it->first << " " << it->second << endl;
}
return 0;
}
注意:
- 判断map中key值是否存在:
if(mp1.find(s) != mp1.end())
- map的遍历:
map<string, int>::iterator it; //迭代器
for(it = gang.begin(); it != gang.end(); it++){
cout << it->first << " " << it->second << endl;
}
3、将姓名(string)映射为编号(int)的方法:
方法1:
设置两个map:
map<string, int> mp1; //姓名 <-> 编号
map<int, string> mp2; //编号 <-> 姓名
int getid(string s){ //获得姓名对应的编号
if(mp1.find(s) != mp1.end()) { //已经出现过
return mp1[s];
}
else{
mp1[s] = num;
mp2[num] = s;
return num++;
}
}
方法2:
用hash的方法把string映射为int:
int population = 0;
map<string, int> indexs;
string names[maxn * 2];
// 获得下标
int getIndex(string s){
if(indexs.find(s) == indexs.cend()){
indexs[s] = population;
names[population] = s;
return population++;
}
return indexs[s];
}
本文介绍了一种基于电话通话记录识别帮派及其头目的算法,通过并查集和深度优先搜索(DFS)两种方法实现,旨在从大量通话数据中找出符合特定条件的帮派群体及其领导人物。

被折叠的 条评论
为什么被折叠?



