BZOJ 2429 聪明的猴子 (最小生成树 kruskal)

BZOJ 2429 聪明的猴子

Description
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。
在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。

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

Input
第1行为一个整数,表示猴子的个数M(2<=M<=500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1–1000之间);
第3行为一个整数表示树的总棵数N(2<=N<=1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000–1000)。
(同一行的整数间用空格分开)

Output
包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数

Sample Input

4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2

Sample Output

3

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

思路:
因为最小生成树的性质满足最大边最小,“能所有树冠上觅食”,所以一定这个猴子一定可以经过最小生成树的最大边。
所以只需记录最小生成树的最大边,统计有多少只猴子能够跨越这个最大边即可。

#include<cstdio>
#include<algorithm>
using namespace std;

struct point{
    int x, y;
}pt[100010];

struct edge{
    int u,  v, dis;
}ed[1000010];

int num=0, n, m, ans=0, lim=0;
int a[100010], book[1000010], f[1000010];
int etot = 0;

bool cmp(const edge &a, const edge &b){
    return a.dis < b.dis;
}

int caldis(int i, int j){
    return (pt[i].x - pt[j].x) * (pt[i].x - pt[j].x) + (pt[i].y - pt[j].y) * (pt[i].y - pt[j].y);
}

void add(int u, int v){
    ed[++num].u = u;
    ed[num].v = v;
    ed[num].dis = caldis(u, v);
}

int getfather(int x){
    if(x == f[x]) return x;
    return f[x] = getfather(f[x]);
}

void Kruscal(){
    sort(ed+1, ed+num+1, cmp);
    for(int i=1; i<=n; i++) f[i] = i;
    for(int i=1; i<=num; i++){
        int y = getfather(ed[i].u);
        int x = getfather(ed[i].v);
        if(x != y){
            ++etot;
            f[y] = x;   
        }
        if(etot == n-1) {//完成了一棵树 
           lim = ed[i].dis;//最大边 
           break;   
        }
    }
}

int main(){
    scanf("%d", &m);
    for(int i=1; i<=m; i++)
        scanf("%d", &a[i]);
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
        scanf("%d%d", &pt[i].x, &pt[i].y);
    for(int i=1; i<n; i++)
        for(int j=i+1; j<=n; j++)
            add(i, j);
    Kruscal();
    for(int i=1; i<=m; i++)
        if(a[i] * a[i] >= lim) ans++;//统计 
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值