文章目录
A - Wireless Network
一开始觉得会超时,后来发现时限是10s,就直接开始暴力
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e3 + 5;
int fa[N]; // 记录每个节点的父节点
int x[N], y[N];
bool rep[N];
double dis[N][N];
double cal(int i, int j)
{
return sqrt(pow(x[i] - x[j], 2.0) + pow(y[i] - y[j], 2.0));
}
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int t, n, d;
int p, q;
scanf("%d%d", &n, &d);
init(n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &y[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
dis[i][j] = dis[j][i] = cal(i, j);
}
}
char op[2];
while (scanf("%s", op) != EOF){
if (op[0] == 'O') {
scanf("%d", &p);
rep[p] = 1;
for (int i = 1; i <= n; i++) {
if (i != p && rep[i] == 1 && dis[i][p] <= d) {
unite(p, i);
}
}
} else {
scanf("%d%d", &p, &q);
if (find(p) == find(q)) {
puts("SUCCESS");
} else {
puts("FAIL");
}
}
}
return 0;
}
B - The Suspects
0号为疑似患者,已知有m个群体,和疑似患者同属一个群体的人都是疑似患者,求有多少疑似患者
模板题,合并后求有多少个人和0号在一个群体内
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
int fa[N]; // 记录每个节点的父节点
void init(int n)
{
for (int i = 0; i < n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int n, m;
int k, x, y;
while (scanf("%d%d", &n, &m) != EOF && n) {
init(n);
while (m--) {
scanf("%d", &k);
for (int i = 0; i < k; i++) {
if (i == 0)
scanf("%d", &x);
else
scanf("%d", &y);
if (i != 0) {
unite(x, y);
}
}
}
int res = 0;
for (int i = 0; i < n; i++) {
if (find(i) == find(0)) {
res++;
}
}
printf("%d\n", res);
}
return 0;
}
C - How Many Tables
并查集模板题,合并后求有多少个根节点
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 5e4 + 5;
int fa[N]; // 记录每个节点的父节点
void init(int n)
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int t, n, m;
int x, y;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
init(n);
for (int i = 0; i < m; i++) {
scanf("%d%d", &x, &y);
unite(x, y);
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (find(i) == i)
ans++;
printf("%d\n", ans);
}
return 0;
}
D - How Many Answers Are Wrong
带权并查集,利用边权的差来表示区间和
d 数组表示权值,因为给出的是一段区间的两个端点 a、b 和 [a, b] 的区间和,我们要找到 a、b 的一个共同基准点,再使用 d[b] - d[a - 1] 来表示 [a, b] 的区间和。
如何找到一个共同的基准点呢?
可以把每次给出的节点 a - 1 和节点 b 进行合并,此时 a - 1 和 b 就有一个共同的基准点(即它们的根节点),所以就可以巧妙的利用它们边权的差来表示区间和
这里主要讲一下带权并查集的 unite 操作:
已知:fa[x] = fx, fa[x] = fy
合并节点 x 和 y 后,使 fy 作为 fx 的父节点。
题目中给出 x 到 y 的距离为 s
有两条路径从 x 到 fy:
1、x ——> fx ——> fy
2、x ——> y ——> fy
这两条路的权值是相等的,即:d[x] + d[fx] = s + d[y]
因为之前合并了 fx 和 fy,fx 作为子节点,根据上式可以更新 d[fx] = d[y] - d[x] + s
所以每次读入 a b 后,先判断 a b 是否同属一个集合:
1、如果属于一个集合,再判断 d[b] - d[a - 1] 是否等于 s,如果不等于,说明回答是错误的。
2、如果不属于一个集合,就合并 a - 1 和 b 两个集合
一位大佬的博客:https://blog.csdn.net/TheSunspot/article/details/107768182
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 2e5 + 5;
int fa[N], d[N];
void init(int n)
{
for (int i = 0; i <= n; i++) {
fa[i] = i;
d[i] = 0;
}
}
int find(int x)
{
if (x == fa[x]) return x;
int root = find(fa[x]);
d[x] += d[fa[x]];
return fa[x] = root;
}
void unite(int x, int y, int s)
{
int fx = find(x), fy = find(y);
fa[fx] = fy; d[fx] = d[y] - d[x] - s;
}
int main(void)
{
int n, m;
int a, b, s;
int res;
while (scanf("%d%d", &n, &m) != EOF) {
init(n);
res = 0;
while (m--) {
scanf("%d%d%d", &a, &b, &s);
if (find(a - 1) == find(b) && d[b] - d[a - 1] != s) {
res++;
} else {
unite(a - 1, b, s);
}
}
printf("%d\n", res);
}
return 0;
}
G - Supermarket
贪心+并查集
贪心策略:先选取利润最大的商品,如果利润相等,优先选择截止时间长的商品
以每个时间点为根,最初时间点 x 是根节点,说明 x 没有被占用,则可以使用时间点 x。使用之后,合并 x 和 x - 1 所在的集合。此时 x 的根节点会变成 x-1,下次再进行 find 操作,会找到 x-1。依次类推,直到最后没有可用的时间点。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10005;
int fa[N];
struct pro{
int p, t;
}arr[N];
void init(int n)
{
for (int i = 0; i <= n; i++)
fa[i] = i;
}
int find(int x)
{
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
bool cmp(pro a, pro b)
{
if (a.p == b.p)
return a.t > b.t;
return a.p > b.p;
}
int main(void)
{
int n, res, maxv, root;
while (scanf("%d", &n) != EOF) {
maxv = res = 0;
for (int i = 0; i < n; i++) {
scanf("%d%d", &arr[i].p, &arr[i].t);
maxv = max(maxv, arr[i].t);
}
init(maxv);
sort(arr, arr + n, cmp);
for (int i = 0; i < n; i++) {
root = find(arr[i].t);
if (root > 0) {
res += arr[i].p;
fa[root] = root - 1;
}
}
printf("%d\n", res);
}
return 0;
}
J - A Bug’s Life
用一个opp数组,来记录每个虫子的异性。
看每次输入的两只虫子是否在一个集合里,如果不在,说明为异性,如果在一个集合里,说明是同性。
已知异性的异性为同性,所以可以将虫子和异性的异性进行合并。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 20005;
int fa[N], opp[N];
void init(int n)
{
for (int i = 1; i <= n; i++) {
fa[i] = i;
opp[i] = 0;
}
}
int find(int x)
{
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
int main(void)
{
int t, n, m;
int a, b, flag;
scanf("%d", &t);
for (int idx = 1; idx <= t; idx++) {
scanf("%d%d", &n, &m);
init(n);
flag = 0;
while (m--) {
scanf("%d%d", &a, &b);
if (find(a) == find(b)) {
flag = 1;
} else {
if (opp[a] == 0) {
opp[a] = b;
} else {
unite(opp[a], b);
}
if (opp[b] == 0) {
opp[b] = a;
} else {
unite(opp[b], a);
}
}
}
printf("Scenario #%d:\n", idx);
if (flag == 1) {
puts("Suspicious bugs found!");
} else {
puts("No suspicious bugs found!");
}
puts("");
}
return 0;
}