并查集思想
本题也可以使用并查集解决。在使用并查集时,只要注意合并函数中需要总是保持点权更大的结点为集合的根结点(原先的合并函数是随意指定其中一个根结点为合并后集合的根结点),就能符合题目的要求。
而为了达到题目对总边权与成员人数的要求,需要定义两个数组:
一个数组用来存放以当前结点为根结点的集合的总边权(totalWeight)。
另一个数组用来存放以当前结点为根结点的集合中的成员人数(gangs)。
这样当所有通话记录合并处理完毕后,这两个数组就自动存放了每个集合的总边权和成员人数,再根据题意进行筛选即可。 (摘自算法笔记)
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
const int MAXN = 2000;
map<string, int> mp1; // name ---> index
map<int, string> mp2; //index ----> name
vector<int> s(MAXN, -1); //并查集数组
vector<int> weight(MAXN, 0); //存放每个顶点的点权值
vector<int> totalWeight(MAXN, 0); //顶点为根节点的集合的总边权值
vector<int> gangs[MAXN]; //gangs[root] 存放以root为根的该集合的所有元素
map<string, int> result; //head ---> 人数
int idx = 0;
int stringToInt(string s) {
if (mp1.find(s) != mp1.end()) {
return mp1[s];
}
else {
mp1[s] = idx;
mp2[idx] = s;
return idx++;
}
}
int Find(vector<int>& s, int x) {
int root = x;
while (s[root] >= 0) {
root = s[root];
}
while (x != root) {
int t = s[x];
s[x] = root;
x = t;
}
return root;
}
void Union(vector<int>& s, int a, int b, int w) {
int roota = Find(s, a);
int rootb = Find(s, b);
if (roota == rootb) {
totalWeight[roota] += w; //更新顶点为根节点的集合的总边权值
return;
}
if (s[roota] <= s[rootb]) {
s[roota] += s[rootb];
s[rootb] = roota;
totalWeight[roota] += totalWeight[rootb] + w;
}
else {
s[rootb] += s[roota];
s[roota] = rootb;
totalWeight[rootb] += totalWeight[roota] + w;
}
}
void Initial() { //输入第二组测试数据时,恢复全局变量状态
for (int i = 0; i < MAXN; i++) {
s[i] = -1;
weight[i] = 0;
totalWeight[i] = 0;
gangs[i].clear();
result.clear();
mp1.clear();
mp2.clear();
}
}
int main()
{
int N, K;
while (cin >> N >> K) {
Initial();
string s1, s2;
int w;
while (N--) {
cin >> s1 >> s2 >> w;
int u = stringToInt(s1);
int v = stringToInt(s2);
Union(s, u, v, w);
weight[u] += w; //更新顶点的点权值
weight[v] += w;
}
for (int i = 0; i < idx; i++) { //将每个人加入自己所在的集合,gangs[root]数组
int id = Find(s, i);
gangs[id].push_back(i);
}
int cnt = 0;
for (int i = 0; i < idx; i++) {
if (totalWeight[i] <= K || gangs[i].size() <= 2) continue;
int h = i;
for (auto p : gangs[i]) { //找到该集合中点权值最大的人的index
if (weight[p] > weight[h]) {
h = p;
}
}
result[mp2[h]] = gangs[Find(s, h)].size(); //gangs[root]中存着h所在集合的人的总数
cnt++;
}
cout << cnt << endl;
for (auto it = result.begin(); it != result.end(); it++) {
cout << it->first << " " << it->second << endl;
}
}
return 0;
}
Union函数修改
void Union(vector<int>& s, int a, int b, int w) {
int roota = Find(s, a);
int rootb = Find(s, b);
if (roota == rootb) {
totalWeight[roota] += w; //更新顶点为根节点的集合的总边权值
return;
}
if (weight[roota] >= weight[rootb]) {
s[rootb] = roota;
totalWeight[roota] += totalWeight[rootb] + w;
}
else {
s[roota] = rootb;
totalWeight[rootb] += totalWeight[roota] + w;
}
}