文章目录
天梯并查集
这是天梯中很喜欢出的一类题。虽然好久没出了hhhhh
L2-007 家庭房产 (25 分) (往结点编号小的合并)
题目分析
给定每个人的家庭成员和其自己名下的房产,请你统计出每个家庭的人口数、人均房产面积及房产套数。
原题链接
能想到是并查集并不困难,但处理数据的输入输出还是有一定的难度
C++
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
struct Data {
int id, fid, mid, num, area;
int cid[10];
}dat[N];
struct Ans {
int id, people;
double num, area;
bool flag; // 设置标记保证不出现除以0的情况
bool operator< (const Ans& W) const {
if (area == W.area) return id < W.id;
return area > W.area;
}
}ans[N];
int n;
int p[N];
bool st[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 保留家庭成员的最小编号
void merge(int a, int b) {
a = find(a), b = find(b);
if (a < b)
p[b] = a;
else if (a > b)
p[a] = b;
}
int main() {
cin >> n;
for (int i = 1; i < N; i++) p[i] = i;
for (int i = 0; i < n; i++) {
int id, fid, mid, k;
cin >> id >> fid >> mid >> k;
st[id] = true;
dat[i].id = id, dat[i].fid = fid, dat[i].mid = mid;
if (fid != -1) {
st[fid] = true;
merge(id, fid);
}
if (mid != -1) {
st[mid] = true;
merge(id, mid);
}
for (int j = 0; j < k; j++) {
int x;
cin >> x;
dat[i].cid[j] = x;
st[x] = true;
merge(id, x);
}
// 读入房产数及总面积
cin >> dat[i].num >> dat[i].area;
}
// 遍历读入的数据,累计房产和面积
for (int i = 0; i < n; i++) {
int id = find(dat[i].id);
ans[id].id = id;
ans[id].flag = true;
ans[id].num += dat[i].num;
ans[id].area += dat[i].area;
}
// 统计人数
for (int i = 0; i < N; i++) {
if (st[i]) {
int family = find(i);
ans[family].people++;
}
}
// 计算人均并统计有多少个家庭
int cnt = 0;
for (int i = 0; i < N; i++) {
if (ans[i].flag) {
cnt++;
ans[i].num /= ans[i].people;
ans[i].area /= ans[i].people;
}
}
sort(ans, ans + N);
cout << cnt << endl;
for (int i = 0; i < N; i++) {
if (ans[i].flag) {
printf("%04d %d %.3f %.3f\n", ans[i].id, ans[i].people, ans[i].num, ans[i].area);
}
}
return 0;
}
L2-010 排座位 (25 分)
题目描述
这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
原题链接
这道题要分析的是要把哪一种关系用并查集进行维护。
上述加粗部分提示到,朋友的朋友也是朋友,说明我们可以维护一个朋友的集合,来便于查询。
而敌对关系我们用一个数组
来记录即可
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int p[N], ene[N][N];
int n, m, k;
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) p[i] = i;
while (m--) {
int a, b, c;
cin >> a >> b >> c;
if (c == 1) {
a = find(a), b = find(b);
p[a] = b;
} else if (c == -1) {
ene[a][b] = -1, ene[b][a] = -1;
}
}
while (k--) {
int a, b;
cin >> a >> b;
bool isFri = find(a) == find(b) ? true:false;
if (ene[a][b] == -1 && isFri) puts("OK but...");
else if (!isFri && ene[a][b] != -1) puts("OK");
else if (isFri && ene[a][b] != -1) puts("No problem");
else puts("No way");
}
return 0;
}
L2-024 部落 (25 分)
在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。
原题链接
圈子内部直接调用并查集,如果圈子与圈子之间有公共编号,则会使两个圈子合并
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n, q;
int p[N];
bool st[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
for (int i = 1; i <= N; i++) p[i] = i;
cin >> n;
set<int> S;
for (int i = 0; i < n; i++) {
int k, x;
cin >> k >> x;
st[x] = true;
S.insert(x);
for (int j = 0; j < k - 1; j++) {
int y;
cin >> y;
S.insert(y);
st[y] = true;
p[find(y)] = find(x);
}
}
int cnt = 0;
for (int i = 1; i <= N; i++) {
if (p[i] == i && st[i]) cnt++;
}
cout << S.size() << ' ' << cnt << endl;
cin >> q;
while (q--) {
int a, b;
cin >> a >> b;
if (find(a) == find(b)) puts("Y");
else puts("N");
}
return 0;
}
L3-003 社交集群 (30 分)
题目描述
给出每个人的兴趣编号,拥有共同兴趣的人在同一个圈子。
问有多少个圈子,每个圈子里有多少人。
一开始在犹豫到底对人进行合并,还是对兴趣点进行合并。没有第一时间思考出来
对兴趣点进行合并,每个人保留第一个兴趣。在所有兴趣合并完成后,遍历每一个的兴趣,从而确定其对应的圈子,并统一圈子个数,及圈子内部人员个数
C++
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int p[N];
int n;
vector<int> v;
struct Node {
bool flag;
int peo;
bool operator< (const Node& w) const {
return peo > w.peo;
}
}ans[N];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
for (int i = 1; i < N; i++) p[i] = i;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int k, h;
scanf("%d: %d", &k, &h);
v.push_back(h);
if (k == 1) continue;
for (int j = 0; j < k - 1; j++) {
int h2;
cin >> h2;
p[find(h2)] = find(h);
}
}
for (auto t: v) {
ans[find(t)].flag = true;
ans[find(t)].peo++;
}
int cnt = 0;
for (int i = 1; i < N; i++) {
if (ans[i].flag) cnt++;
}
cout << cnt << endl;
sort(ans, ans + N);
for (int i = 0; i < cnt; i++) {
if (!i) cout << ans[i].peo;
else cout << ' ' << ans[i].peo;
}
return 0;
}