P2504 [HAOI2006]聪明的猴子 题解

题目

链接

https://www.luogu.com.cn/problem/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包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。

样例 #1

样例输入 #1
4
 1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
样例输出 #1
3

提示

【数据规模】

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

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

感谢@charlie003 修正数据

思路

此题为一道最小生成树的模板题
因为题目出现了 所有树冠上觅食 所有树冠上觅食 所有树冠上觅食
为了保证跳跃距离最小,所以添加的边数要尽可能地小。最小生成树地构图边数最小为n-1;
满足了最小生成树的一切特征
重点是算欧氏距离建图 重点是算欧氏距离建图 重点是算欧氏距离建图
最后只需要记录建成图的边的距离最大值与猴子跳跃距离比较统计即可

代码实现

#include<bits/stdc++.h>
using namespace std;

const int maxm=500+10;
const int maxn=1e3+10;
const int maxl=1e6+10;
int n,m,cnt,tot;
double ans;
int s[maxn],e[maxn],fa[maxn],o[maxm];
struct node{
	int u,v;
	double w;
}a[maxl];
inline double distance(int x1,int y1,int x2,int y2){return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}//欧氏距离
//Kruskal模板
inline void add(int u,int v,double w){
	a[++cnt].w=w;
	a[cnt].u=u;
	a[cnt].v=v;
}
inline void init(){
	for(int i=1;i<=n;i++)fa[i]=i;
}
inline bool cmp(node x,node y){return x.w<y.w;}
inline int find(int x){
	if(fa[x]==x)return x;
	return find(fa[x]);
}
inline bool merge(int x,int y){
	int p=find(x);
	int q=find(y);
	if(p==q)return false;
	fa[p]=q;
	return true;
}
inline void Kruskal(){
	init();
	sort(a+1,a+cnt+1,cmp);
	for(int i=1;i<=cnt;i++){
		if(merge(a[i].u,a[i].v)){
			//求最小长度的最大距离
			ans=max(ans,a[i].w);
			n--;
			if(n==1)return ;
		}
	}
}
int main(){
	scanf("%d",&m);
	for(int i=1;i<=m;i++)scanf("%d",&o[i]);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d",&s[i],&e[i]);
	//建图
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			double dis=distance(s[i],e[i],s[j],e[j]);
			//printf("dis = %lf\n",dis);
			add(i,j,dis);
			add(j,i,dis);
		}
	}
	Kruskal();
	//统计输出
	for(int i=1;i<=m;i++){
		if(o[i]>=ans)tot++;
	}
	printf("%d\n",tot);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

materialistOier

我只是一名ssfoier

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值