洛谷P2504《[HAOI2006]聪明的猴子》

最小生成树板子题

题目描述

在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。

现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。

在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。

【问题】现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。

输入输出格式

输入格式

输入文件monkey.in包括:

第1行为一个整数,表示猴子的个数M(2<=M<=500);

第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1–1000之间);

第3行为一个整数表示树的总棵数N(2<=N<=1000);

第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000–1000)。

(同一行的整数间用空格分开)

输出格式

输出文件monkey.out包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。

输入输出样例

输入样例#1

1
2
3
4
5
6
7
8
9
4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2

输出样例#1

1
3

说明

【数据规模】

对于40%的数据,保证有2<=N <=100,1<=M<=100

对于全部的数据,保证有2<=N <= 1000,1<=M=500

感谢@charlie003 修正数据

解题思路

先构造完全图(边数为初二数学内容),再跑一遍最小生成树

之后枚举每一个猴子,判断它的跳跃距离是否大于等于生成树的最大边权即可

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>

#define FILE_IN(__fname) freopen(__fname, "r", stdin)
#define FILE_OUT(__fname) freopen(__fname, "w", stdout)
#define IMPROVE_IO() std::ios::sync_with_stdio(false)

using std::cin;
using std::cout;
using std::endl;

const int MAXN = 1000 + 10;
const int MAXM = 500 + 10;
const int MAXE = (MAXN - 1) * MAXN / 2 + 10;

struct Node {
int x, y;

Node() { x = y = 0; }
} node[MAXN];

struct Edge {
int previd, nextid;
double weight;

Edge() { previd = nextid = 0; weight = 0; }
bool operator < (const Edge &that) const {
return weight < that.weight;
}
} edge[MAXE];

struct UnionFind {
int seq[MAXN];

UnionFind() { memset(seq, 0, sizeof seq); }
int Find(int x) { return !seq[x] ? x : (seq[x] = Find(seq[x])); }
bool Union(int x, int y) {
x = Find(x); y = Find(y);
if (x == y) return false;
seq[x] = y;
return true;
}
} U;

double GetDist(int idx, int idy) {
double ret = 0;
int absx = std::abs(node[idx].x - node[idy].x);
int absy = std::abs(node[idx].y - node[idy].y);
ret = sqrt(absx * absx + absy * absy);
return ret;
}

int n, m, cnt;
int monkey[MAXM];

double Kruskal() {
std::sort(edge + 1, edge + 1 + cnt);
int tot = 0;
double maxWeight = 0;
for (int i = 1; i <= cnt; ++i) {
if (U.Union(edge[i].previd, edge[i].nextid)) {
++tot;
maxWeight = std::max(maxWeight, edge[i].weight);
}
if (tot == n - 1) break;
}
return maxWeight;
}

int main() {
IMPROVE_IO();
cin >> m;
for (int i = 1; i <= m; ++i) cin >> monkey[i];
cin >> n;
for (int i = 1; i <= n; ++i) cin >> node[i].x >> node[i].y;
// initialize edges
for (int i = 1; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
edge[++cnt].previd = i;
edge[cnt].nextid = j;
edge[cnt].weight = GetDist(i, j);
}
}
double maxW = Kruskal();
int ans = 0;
for (int i = 1; i <= m; ++i) {
if (monkey[i] >= maxW) ++ans;
}
cout << ans << endl;
return 0;
}