题目传送门:【POJ 2349】
题目大意:国防部(DND)要用无线网络连接北部几个哨所。两种不同的通信技术被用于建立网络:每一个哨所有一个无线电收发器,一些哨所将有一个卫星频道。
任何两个有卫星信道的哨所可以通过卫星进行通信,而不管他们的位置。同时,当两个哨所之间的距离不超过 D 时可以通过无线电通讯,D 取决于对收发器的功率。功率越大,D 也越大,但成本更高。出于采购和维修的方便,所有哨所的收发器必须是相同的;那就是说,D 值对每一个哨所相同。
你的任务是确定收发器的 D 的最小值。每对哨所间至少要有一条通信线路(直接或间接)。
题目分析:
一道略考思维的 Kruskal 题目。
由于我们在最开始的时候不知道哪些哨所拥有卫星通信设备,所以为了求出最后的答案,我们先要将所有的哨所之间的距离求出来(相当于构造一个完全图)。
之后,我们可以根据题意,用 Kruskal 求出它的最大边权值作为收发器的 D 值。
但是本题有一个特点:能够进行卫星通讯的两点不用考虑它的边权!
所以,在这个算法中,原本被选中的最后 S 条边就可以忽略不计。
例如:P = 5 , 5 个哨所的坐标分别为 (0 , 125) , (100 , 0) , (300 , 0) , (100 , 250) , (200 , 200) 。每条边的权值从小到大排序为 {111.80 , 160.08 , 160.08 , 200 , 213.60 , 223.61 , 223.61 , 250 , 320.16 , 325};
当 S = 0 时,按照 Kruskal 的方法,此时得到的最大边权为 200;但当 S = 3 时,结果变为了第 (5 - 3) = 2 条边的边权 (160.08) 。
下面附上代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MX = 1005;
struct Edge{
int u,v;
double len;
};
Edge edge[MX*MX];
int s,p,te = 0,cnt = 0; //te:总边数
int locat[MX][2],fa[MX];
double map[MX][MX],res[MX];
bool comp(const Edge& i,const Edge &j){
return i.len < j.len;
}
double cdis(int a1,int a2,int b1,int b2){ //得到两个点之间的距离
double x = ((b1-a1) * (b1-a1) + (b2-a2)*(b2-a2));
return sqrt(x);
}
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
void kruskal(){
int k = 0;
for (int i = 1;i <= p;i++) fa[i] = i;
sort(edge + 1,edge + te + 1,comp);
for (int i = 1;i <= te;i++){
int x = find(edge[i].u), y = find(edge[i].v);
if (k >= p-1) break;
if (x != y){
res[cnt++] = edge[i].len;
k++;
fa[x] = y;
}
}
}
int main(){
int T;
cin>>T;
while (T--){
memset(locat,0,sizeof(locat));
memset(map,0,sizeof(map));
memset(edge,0,sizeof(edge));
te = 0, cnt = 0;
cin>>s>>p;
for (int i = 1;i <= p;i++){
cin>>locat[i][0]>>locat[i][1];
}
for (int i = 1;i <= p;i++){
for (int j = 1;j <= p;j++){
map[i][j] = cdis(locat[i][0],locat[i][1],locat[j][0],locat[j][1]);
if (i <= j){
edge[++te].u = i;
edge[te].v = j;
edge[te].len = map[i][j];
}
}
}
kruskal();
printf("%.2lf\n",res[cnt - s]);
}
return 0;
}