|
题目大意
给出1000条以内的通话记录A B和权值w(通话时间),和阈值k。现在定义:如果一个团伙人数超过2人并且通话总权值超过k,则列为怀疑对象,其中团伙里自身权值最大的为头目。
输出所有满足条件的团伙的头目,和他们团伙里面的人数。
思路解析
本题比较简单的方法是利用图的遍历,求出各连通分量(极大联通子图),如果联通分量内部节点的个数大于2,且总权值超过K则为所求,找出头目输出就可以。所以主要前提就是把Name转换成下标,才能利用图解决问题。笔者所用的方案是利用index作为计数器,每发现一个从未出现过的name,就用index作为他的下标,同时index++。这样就对应了一张从name映射到id的map表,我为了方便多建立了一个从id到name的map表。剩下的就是基本的深度优先遍历了。大家可以根据需要自行修改遍历代码,方案不唯一。
示例代码
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
int index = 0;
map<string, int> table;
map<int, string> rtable;
int gra[2001][2001];
bool visited[2001];
int weight[2001] = { 0 };
void toint(string s) {//为每一个name分配独一无二的编号
if (table.find(s) == table.end()) {
rtable[index] = s;
table[s] = index++;
}
}
void dfs(int v, vector<int>& vec, int& sum) {
visited[v] = true;
sum += weight[v];
vec.push_back(v);
for (int i = 0; i <= index; i++) {
if (!visited[i] && gra[v][i] == 1)
dfs(i, vec, sum);
}
}
int main() {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++) {
string s1, s2;
int c;
cin >> s1 >> s2 >> c;
toint(s1); toint(s2);
int a = table[s1], b = table[s2];
gra[a][b] = gra[b][a] = 1;
weight[a] += c;
weight[b] += c;
}
vector<string> res;//存放所有头目
map<string, int> mapp;//头目对应集团人数
for (int i = 0; i < index; i++) {
if (!visited[i]) {
vector<int> vec;//存储团伙里所有的成员(只统计个数,也可以用计数器记录)
int sum = 0;//通话总时长,每遍历一个就要更新一次
dfs(i, vec,sum);
if (vec.size() > 2 && sum > 2*k) {
int max = 0; int t;//选出head
for (int j = 0; j < vec.size(); j++) {
if (weight[vec[j]] > max) {
max = weight[vec[j]];
t = vec[j];
}
}
res.push_back(rtable[t]);
mapp[rtable[t]] = vec.size();
}
}
}
printf("%d\n", res.size());
sort(res.begin(), res.end());
for (int i = 0; i < res.size(); i++)
cout << res[i] << " " << mapp[res[i]] << endl;
return 0;
}