原题链接:https://www.luogu.org/problem/show?pid=1991
首先此题给出的边并不是常规的形式,而是坐标式,所以要枚举所有的点的情况,求出所有的边的边长。
很显然,用最小生成树能够求出所有点联通时的最长边的距离,但是因为有无线电收发器的情况,所以可以免去一些边的选用。
之后考虑配备无线电收发器的情况,假设我们已经有了最小生成树,如果只有一个收发器,那么相当于没有,它自己是派不上用场的。
如果有两个收发器,很显然,应该装在最长的边的两个端点处,这样就可以减少一条最长的边。
第三台怎么办呢?找到一个最长的边,任意选它的一个端点,将它与现有的有收发器的点之间再连一条边,将这条边的长度记为0,于是这条边也可以忽略不计了。第四台,第五台以此类推。。。
这样就能发现,一共需要在原最小生成树上选最短的n-s条边(s为无线电收发器的个数)。
最后说一点优化,为了防止精度问题,同时又因为边权比较小,所有可以存储边的距离的平方,将最后结果开方输出便可。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; void read(int &y) { y=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') { y=y*10+x-'0'; x=getchar(); } } int x[505],y[505],f[505],cnt; struct edge { int u,v,w; }e[150005]; int find(int x) { if(f[x]==x) return x; return f[x]=find(f[x]); } bool cmp(edge a,edge b) { return (a.w<=b.w); } int main() { int s,p; read(s);read(p); int t=p-s; for(int i=1;i<=p;i++) { read(x[i]);read(y[i]); f[i]=i; } for(int i=1;i<=p;i++) { for(int j=i+1;j<=p;j++) { e[++cnt].w=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); e[cnt].u=i; e[cnt].v=j; } } sort(e+1,e+cnt+1,cmp); for(int i=1;i<=cnt;i++) { int a=find(e[i].v),b=find(e[i].u); if(a==b) continue; f[find(b)]=a; t--; if(t==0) { double ans=sqrt(e[i].w); printf("%.2lf",ans); return 0; } } return 0; }