题意:
n个站点,s个卫星系统,每个卫星系统只能安排在一个站点
有卫星系统的站点间通讯不需要代价
任意两点(i, j)间皆可通讯,代价为dis[i][j]
找到最小的代价使得任意两个站点间均可以通讯
n, s <= 1000
kruskal
1、把所有边按从小到大的顺序排序,遍历每条边(u,v)
2、u和v在同一个连通分量中,那么加入(u,v)会成环,不能选择。
3、如果u和v在不同的连通分量,那么(u,v)一定是最优的。 用反证法证。
#include <iostream>
#include <cstdio> //题意 注意 是除了删掉的点以外 最大的边
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 500005; //边的个数
int u[maxn], v[maxn];
double w[maxn], ans[maxn]; //分别代表第i条边的两个端点序号,和权值
int pr[maxn]; // 找父节点
int r[maxn]; //排序后第i小的边的序号保存在r[i]中
int s, p, m, k; //p个点 m条边
struct Pos
{
int x, y;
}pos[maxn];
double getdis(int i, int j)
{
return sqrt((pos[i].x-pos[j].x)*(pos[i].x-pos[j].x) + (pos[i].y-pos[j].y)*(pos[i].y-pos[j].y));
}
bool cmp(const int i, const int j)
{
return (w[i] < w[j]);
}
int Find(int x)
{
return pr[x] == x ? x : pr[x] = Find(pr[x]);
}
void kruskal() // 如果u,v 在不同的连通分量,那么加入(u,v)一定是最优的
{
for(int i=0; i<p; i++) //初始化并查集
pr[i] = i;
for(int i=0; i<m; i++) //初始化边序号
r[i] = i;
sort(r, r+m, cmp);
k=0;
for(int i=0; i<m; i++)
{
int e = r[i];
int x = Find(u[e]); //当前边的端点所在的集合
int y = Find(v[e]);
if(x != y)
{
ans[k++] = w[e];
pr[x] = y;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&s,&p);
m = 0;
for(int i=0; i<p; i++)
{
scanf("%d%d",&pos[i].x, &pos[i].y);
}
for(int i=0; i<p-1; i++)
{
for(int j=i+1; j<p; j++)
{
u[m] = i;
v[m] = j;
w[m++] = getdis(i,j);
}
}
kruskal();
sort(ans, ans+k-1);
printf("%.2f\n",ans[k-s]);
}
return 0;
}