0
题意很简单,要求每个站点之间都要可以直接或者间接联系到,但是联系方式有两种,近距离的用无线(每个站点都有),远距离的用卫星(部分站点有卫星,且当两个站点都有卫星才可以用卫星直接联系),给出卫星站点数量,要求输出最大的两个无线站点之间的距离。
1
读完题意很容易想到最小生成树,但是卫星站点应该放到哪里?是先把距离最远的几个站点分配为卫星站点,再用Prim;还是先用Prim再将最小生成树中的各条边中最大的几条边所接触的站点作为卫星站点?
因为这一点不清楚,一直没有立即写,后来从别的博客上看到这样一个思想。我们假设当前已经得到了我们想要的结果,即已经把所有站点连接起来,相互距离很近的站点之间用无线,距离远的用卫星,则形成了m(假设是m)个站点群,站点群内部用无线联系,站点群之间用卫星来互相联系,那么站点群的个数就是卫星的个数,加上站点群个数-1条边即卫星联系的边,则构成完整的联系图。那么如果将卫星站点联系的边去掉,那么图将变为m个连通支。去掉的用卫星联系的边不就是因为距离大于d的边么。
后来看到这个关于最小生成树的定理: 如果去掉所有权值大于 d 的边后, 最小生成树被分割成为 k 个连通支,图也被分割成为 k 个连通支。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
///Prim—ALGORITHM。想复杂了,要理解最小生成树的性质,即假如将权值大于d的边舍弃掉,最小生成树将会被分割为k个连通支,而去掉的大于d的边不就是我们需要用卫星来互相联系的边么
using namespace std;
double mat[510][510];
double distancs[510];//确定集合到达未确定点的距离,不断取最小
int visted[510];
int point[510][2];
double temp[510];
double Distance(int a,int b){
int x1=point[a][0];
int y1=point[a][1];
int x2=point[b][0];
int y2=point[b][1];
return sqrt( pow((double)x2-x1,2)+pow((double)y2-y1,2) );
}
int main()
{
int kase;
scanf("%d",&kase);
while(kase--){
//memset(mat,0,sizeof(mat));
memset(visted,0,sizeof(visted));
int s,p;
scanf("%d%d",&s,&p);
for(int i=1;i<=p;i++){
scanf("%d%d",&point[i][0],&point[i][1]);
}
for(int i=1;i<=p;i++){
for(int j=1;j<=p;j++){
if(i==j) continue;
mat[i][j]=Distance(i,j);
}
}
for(int i=1;i<=p;i++){
distancs[i]=mat[1][i];
}
visted[1]=1;
int l=0;
double minn=10000000000;
while(l<p-1){
minn=10000000000;
int k2=-1;
for(int i=1;i<=p;i++){
if(visted[i]==0&&distancs[i]<minn){
minn=distancs[i];
k2=i;
}
}
temp[++l]=minn;//必须先记录所有边再排序,因为最小生成树生加入的边总是局部最小的,而并不是按全局有小到大来生成的。
visted[k2]=1;
for(int i=1;i<=p;i++){
if(visted[i]==0&&mat[k2][i]<distancs[i]){
distancs[i]=mat[k2][i];
}
}
}
sort(temp+1,temp+1+l);
printf("%.2f\n",temp[(p-1)-(s-1)]);
}
}