初见安~这里是传送门:洛谷P1991

题目描述

国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;

每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。

任意两个配备了一条卫星电话线路的哨所(两边都有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 D,这是受收发器的功率限制。收发器的功率越高,通话距离 D 会更远,但同时价格也会更贵。

收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 D。你的任务是确定收发器必须的最小通话距离 D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

输入格式:

从 wireless.in 中输入数据第 1 行,2 个整数 S 和 P,S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量。接下里 P 行,每行两个整数 x,y 描述一个哨所的平面坐标(x, y),以 km 为单位。

输出格式:

输出 wireless.out 中

第 1 行,1 个实数 D,表示无线电收发器的最小传输距离,精确到小数点后两位。

输入样例#1: 

2 4
0 100
0 300
0 600
150 750

输出样例#1: 

212.13

 

题解:

这道题很明显——如果没有卫星电话这种操作的话,就是个最小生成树【甚至近乎于模板题】。所谓最小通话距离,就是必须要有的通话线路中最长的一条。所以这里就涉及到有两种方法了:Kruskal和Prim(没学过的话这里有传送门:最小生成树)。那么用哪个呢?我们继续看题——

题目说,要安装指定数目的卫星电话,使安装了卫星电话的各个地方可以不限距离地连接上。很明显——在我们生成最小生成树后,要安装卫星电话就相当于删边,自然而然地我们会想到从最长的边删起。所以就会涉及到排序,我们就用Kruskal——并且直接在生成最小生成树的时候决定生成的树有几条边就可以了。

下面是代码及详解——

#include<bits/stdc++.h>
using namespace std;
int n,s;
int fa[510];
int a[1000][3];
struct node//存边
{
    int u,v;
    double w;
}e[260000];

bool cmp(node a,node b)
{
    return a.w<b.w;
}

int get(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=get(fa[x]);
}

int same(int x,int y)
{
    return get(x)==get(y);
}

void merge(int x,int y)
{
    fa[get(x)]=get(y);
}

int main()
{
    cin>>s>>n;
    int k=0;//统计边数
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;//初始化
        cin>>a[i][1]>>a[i][2];
    }
    
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i!=j)
            {
                e[++k].u=i;e[k].v=j;
                e[k].w=sqrt( (a[i][1]-a[j][1])*(a[i][1]-a[j][1]) + (a[i][2]-a[j][2])*(a[i][2]-a[j][2]) );//存入边,勾股定理
            }
        }
    }
    
    sort(e+1,e+1+k,cmp);//排序
    
    int num=n-s+1;//直接用s修改目标
    double maxn=0.0;
    for(int i=1;i<=k&&num>1;i++)
    {
        int x=e[i].u,y=e[i].v;
        if(same(x,y)) continue;//Kruskal
        else
        {
            merge(x,y);
            num--;
            maxn=max(maxn,e[i].w);
        }
    }

    printf("%.2f",maxn);
    return 0;
}