P2504 聪明的猴子

题目描述

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

现在,在这个地区露出水面的有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包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。

 数据范围

2≤N≤1000,1≤M≤500

思路:

这道题是一道使用最小生成树的克鲁斯卡尔算法的题。我们要用最小生成树来将所有节点连接起来,并求出将它连接所需的最小距离。

在这道题中,我们将所有的树冠的位置上设置为端点。在读入的时候,我们设一个结构体来分别存储每个端点的横坐标和纵坐标。记录下每一个端点后,我们可以O(n²)枚举每两个端点,求出这些端点中任意两个点之间的距离(因为所有端点间都可以连接,所以我们可以直接构造一个完全图),并且再开一个结构体来存储每一条边的起点、终点和距离。接下来,我们要对边权从小到大进行排序。

我们维护一个对每个端点的并查集,从边权从小到大加入边,直到边数足够即可退出循环。在建最小生成树的时候,我们要不断更新最小生成树中的最长边的长度。因为我们已经对边权进行了从小到大的排序,所以后加入的边边权一定比前加入的边边权大,故在更新最值时不用比较。

最后求出最值后,我们再对猴子的最大跳跃距离从大到小进行排序,然后进行O(n)的从前往后枚举,如果枚举到一只猴子最大距离小于最大边权,则退出枚举,同时输出当前猴子的序号减1的值(从当前猴子开始无法全跳过)。或者如果已经枚举到了最后一只猴子,则只需要输出这只猴子序号即可,因为所有猴子都能到达所有树冠。

这里不需要判断能否建成最小生成树,因为我们建图时建立的是完全图,保证每两点间都能联通。

另外,在求和判断距离时我们不必求出实际距离,而是求出实际距离的平方,同时在读入猴子最大跳跃距离时对其取平方,这样可以避免精度问题和类型转换。

最后是完整代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int m;
 5 int dis[505];
 6 int n;
 7 struct tree{
 8     int x,y;
 9 }a[1005]; 
10 int fa[1005];
11 struct street{
12     int start;
13     int end;
14     int val;
15 }b[1000005];
16 bool cmp(street a,street b){
17     return a.val<b.val;
18 }
19 bool cmp2(int a,int b){
20     return a>b;
21 }
22 int find(int x){
23     if(x==fa[x]){
24         return x;
25     }else{
26         return fa[x]=find(fa[x]);
27     }
28 }
29 void unionn(int x,int y){
30     int r1=find(x);
31     int r2=find(y);
32     fa[r1]=r2;
33 }
34 int main(){
35     cin>>m;
36     for(int i=1;i<=m;i++){
37         cin>>dis[i];
38     }
39     sort(dis+1,dis+m+1,cmp2);//从大到小排序 
40     cin>>n;
41     for(int i=1;i<=n;i++){
42         cin>>a[i].x>>a[i].y;
43     }
44     int cnt=1;
45     for(int i=1;i<=n;i++){
46         for(int j=i+1;j<=n;j++){
47             b[cnt].start=i;
48             b[cnt].end=j;
49             b[cnt].val=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y);
50             cnt++;
51         }
52     }
53     cnt--;
54     sort(b+1,b+cnt+1,cmp);
55     for(int i=1;i<=n;i++){
56         fa[i]=i;
57     }
58     int sum=0;
59     int ans=-9999;//加入最小生成树的边的边权最大值
60     for(int i=1;i<=cnt;i++){
61         if(find(b[i].start)!=find(b[i].end)){
62             sum++;
63             ans=max(ans,b[i].val);
64             unionn(b[i].start,b[i].end);
65         }
66         if(sum==n-1){
67             break;
68         }
69     }
70     for(int i=1;i<=m;i++){
71         if(dis[i]*dis[i]<ans){
72             cout<<i-1<<endl;
73             break;
74         }
75         if(i==m){
76             cout<<m<<endl;
77             break;
78         }
79     }
80     return 0;
81 }

猜你喜欢

转载自www.cnblogs.com/qianr/p/13387135.html
今日推荐