注意点:编号转换函数;DFS中记录整个连通图的点权之和,总边权就是总点权的一半。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2010;
unordered_map<int, string> idTostr; //编号->名字
unordered_map<string, int> strToid; //名字->编号
map<string, int> Gang; //团伙头目->团伙人数
int n, k, weight, numPerson = 0; //numPerson记录总人数
int G[maxn][maxn] = {0}, w[maxn] = {0};
bool vis[maxn] = {false};
int change(string s) //编号转换函数
{
if(strToid.find(s) != strToid.end()) { //判断s是否已经记录过
//注意此处不能是if(strToid[s] != 0),因为0编号是存在的
return strToid[s];
} else {
strToid[s] = numPerson;
idTostr[numPerson] = s;
return numPerson++; //先返回编号,总人数再++
}
}
//nowVisit为当前访问的顶点编号,head为团伙头目编号,numMember为连通图人数
//totalPValue为连通图总点权
void DFS(int nowVisit, int& head, int& numMember, int& totalPValue)
{
numMember++;
totalPValue += w[nowVisit];
vis[nowVisit] = true;
if(w[nowVisit] > w[head]) head = nowVisit; //更新head编号
for(int i = 0; i < numPerson; ++i) {
if(G[nowVisit][i]) {
if(!vis[i]) DFS(i, head, numMember, totalPValue);
}
}
}
void DFSTrave() //遍历整个图
{
for(int i = 0; i < numPerson; ++i) {
if(!vis[i]) { //对每个没访问过的顶点
int head = i, numMember = 0, totalPValue = 0;
DFS(i, head, numMember, totalPValue);//访问其所在连通块,并计算head, numMember, totalPValue的值
if(numMember > 2 && totalPValue > 2 * k) {
Gang[idTostr[head]] = numMember;
}
}
}
}
int main()
{
scanf("%d %d", &n, &k);
for(int i = 0; i < n; ++i) {
string s1, s2;
cin >> s1 >> s2;
scanf("%d", &weight);
int u = change(s1), v = change(s2);
G[u][v] = G[v][u] = 1;
w[u] += weight;
w[v] += weight;
}
DFSTrave();
printf("%d\n", Gang.size());
for(auto it = Gang.begin(); it != Gang.end(); ++it) {
cout << it->first << " " << it->second << endl;
}
return 0;
}
并查集做法:
注意点:不需要在Union函数中加入额外判断,先简单合并构造并查集,然后利用set来对每个并查集的所有结点遍历,把点权最大的编号赋值给head
#include <bits/stdc++.h>
using namespace std;
const int N = 2010;
int n, k, father[N], w[N], numPerson = 0;
unordered_map<string, int> str_int;
unordered_map<int, string> int_str;
map<string, int> Gang;
int findFather(int x)
{
if(x == father[x]) return x;
else {
int f = findFather(father[x]);
father[x] = f;
return f;
}
}
void Union(int a, int b)
{
int faA = findFather(a);
int faB = findFather(b);
if(faA != faB) father[faB] = faA;
}
int change(string s)
{
if(str_int.find(s) != str_int.end()) {
return str_int[s];
} else {
str_int[s] = numPerson;
int_str[numPerson] = s;
return numPerson++;
}
}
void init() {
for(int i = 0; i < N; ++i) {
father[i] = i;
w[i] = 0;
num[i] = 0;
}
}
int main()
{
init();
int weight;
scanf("%d %d", &n, &k);
unordered_map<int, unordered_set<int> > mp; //根结点->并查集
for(int i = 0; i < n; ++i) {
string s1, s2;
cin >> s1 >> s2;
int u = change(s1), v = change(s2);
scanf("%d", &weight);
Union(u, v);
w[u] += weight;
w[v] += weight;
}
for(int i = 0; i < numPerson; ++i) {
int f = findFather(i);
mp[f].insert(i);
}
for(auto it = mp.begin(); it != mp.end(); ++it) { //对每个根结点
int head = it->first, total = 0; //head初值为根结点编号,total记录总点权
//遍历并查集中的每个元素
for(auto itr = it->second.begin(); itr != it->second.end(); ++itr) {
total += w[*itr]; //计算总点权
if(w[*itr] > w[head]) { //更新head
head = *itr;
}
}
if(it->second.size() > 2 && total > 2 * k) {
Gang[int_str[head]] = it->second.size();
}
}
printf("%d\n", Gang.size());
for(auto it = Gang.begin(); it != Gang.end(); ++it)
cout << it->first << " " << it->second << endl;
return 0;
}