7th 【并查集&&最小生成树】无线通讯网

来源:互联网 发布:mysql 等于符号 编辑:程序博客网 时间:2024/06/12 01:18

                                               无线通讯网

【题目描述】:

国防部计划用无线网络连接若干个边防哨所。2种不同的通讯技术用来搭建无线网络;每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。

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

收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个D。

你的任务是确定收发器必须的最小通话距离D,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。

【输入描述】:

第1行:2个整数S 和P ,S表示可安装的卫星电话的哨所数,P表示边防哨所的数量。

接下里P行,每行描述一个哨所的平面坐标(x,y),以km为单位。

【输出描述】:

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

【样例输入】:

2 40 1000 3000 600150 750

【样例输出】:

212.13

【时间限制、数据范围及描述】:

时间:1s 空间:64M

对于20%的数据 P=2,S=1

对于另外20%的数据 P=4,S=2

对于100%的数据 1<=S<=100,S<P<=500,0<=x,y<=10000


啦啦啦厉害了我们来看一下这题。


       并查集的运用真的是很方便,我比较菜只能套模板。

 
      在平面上有p个点,其实两两都可以相连,现在给你s次机会,免费连接两个点,问如何连接所有点,使得最长的边最短。

先介绍一种常用的算法,就是把所有点连起来使得边的权值和最小。(最小生成树) 
      我们每次选择一个最短的边,然后看看该边两端的端点,如果这两个点不在一个集合内,就把这两个点放入一个集合内(意为连起来)。如果在个集合内,说明这两个点之间已经相通,那么你这次的连接是不划算的,因为这条边长度比前面的所有边长度都长,替换任意一条边都是不合理的
 
       

那么求树的最小权值边也是这个道理。
       按小到大的顺序排列,每次经过判断后连接两个点。
 
         

那么  来看一看我手画的扭捏的图。 



  首先      3-4这条边最短,那么连接3和4。
                   (所以 f[3]=3,f[4]=3,这里通过并查集把两个点放在了一个集合) 
         其次     选择2-5这条边  因为2和5不在一个集合,所以连接2和5
         继续     选择1-2这条边  因为1和2不在一个集合,所以连接1和2
                   (因为2和5在一个集合,所以通过并查集可以将1,2,5变成一个集合) 
         继续     选择1-4这条边  因为1和4不在一个集合,所以连接1和4
                    (这样1,2,3,4,5都放在了一个集体) 
         接着    选择1-5这条边 因为1和5在一个集合,所以不连接。
                  (因为6>2且6>3)
         最后     选择3-5这条边 因为3和5在一个集合,所以不连接。
             这样就选择了一条最优的连接方式。 


     那么我们看一下这题。

          其实我们排完序后无需连接完整的树,只要连接p-s次就可以了,因为后面的s个可以免费相连。
          那么我们从小到大开始连接。如果两点在一个集合,就跳过该边即可。


         这里的边是隐藏的,我们需要计算每两点的距离。 

   看一下

#include <iostream>  #include <cstring>  #include <cstdio>  #include <algorithm>  #include <cmath>using namespace std;  int s,p;int a[505],b[505];int f[250005];struct edge{    int x,y;//边的起点和中点     double d; //边的长度     }e[250005];//结构体     double dis(int a,int b,int c,int d){    double lon=(double)(sqrt((a-c)*(a-c)+(b-d)*(b-d)));    return lon;    }//两点间的距离     int find(int x){   if(f[x]==x)   return x;   return f[x]=find(f[x]);}    //查找点所在集合 int cmp(edge a,edge b){   return a.d<b.d;     }int main()  {     freopen("test1.in","r",stdin);      freopen("test1.out","w",stdout);    cin>>s>>p;    int k=0;    for(int i=1;i<=p;i++)     cin>>a[i]>>b[i];    for(int i=1;i<=p-1;i++)      for(int j=i+1;j<=p;j++)          {                 e[++k].x=i;              e[k].y=j;              e[k].d=dis(a[i],b[i],a[j],b[j]);                }  //开始生成每条边     for(int i=1;i<=250005;i++)    f[i]=i;        sort(e+1,e+k+1,cmp);//从小到大进行排序         int i,j,fx,fy;     for(i=1,j=1;i<=p-s;j++){  //我们需要连接 p-s条边     fx=find(e[j].x);fy=find(e[j].y);//查找两点 if(fx!=fy){i++;//连接一次     f[fx]=fy;//合并 }} printf("%.2lf\n",e[j-1].d);//结束以后 j-1的值是i次全部连接完后最后一个数的下标。                                //即为所有边的最大值      return 0;  }  

程序

原创粉丝点击