思路:每个哨所都有无线电收发器,故可以看成一颗树 而卫星电话只有m台,为了使只通过无线电收发器通话的哨所之间的距离最短
先不考虑卫星电话,显然最小生成树最优,距离为最小生成树的最小边
卫星电话只能连通m个哨所,在最小生成树上可以把这m个哨所看成一个超级源点,此时最小生成树变成了n-m+1个节点,少了m-1条边
显然为了使距离最短,删去最长的m-1条边
虽然最长的m-1条边连接的哨所不属于同一连通块,但把最长的m-1条边一端连接的哨所P看成超级源点后,这条边就不需要了 p及其子节点通过超级源点与其他哨所连通
和loj10066新的开始很像
注意:1.还好本题m≤n 不然数组下标会越界 n-m<0
#include<bits/stdc++.h> using namespace std; const int N=1.3e5+5; struct P{ int x,y; }p[505]; struct E{ int x,y;//int w double w; /* operator <(E a)const{//重载<形参一个 重载()形参两个 [Error] no match for 'operator<' (operand types are 'E' and 'E') return w<a.w; }*/ }e[N];//ISO C++ 不允许声明无类型的'operator<'[-fpermissive] bool cmp(E a,E b){ return a.w<b.w; } /*struct EDGE{ int next,to,w; operator <(EDGE a){ return w<a.w; } }edge[N]; void add(int u,int v){ edge[++tot].next=head[u]; edge[tot].to=v; edge[tot].w=dist(u,v); //head[v]=u;起点的边集 head[u]=tot; }*/ int tot,head[N],fa[505],rank[505]; //double d[505][505];邻接矩阵也不行 只能给定起点和终点 得到权值 double ans[N]; double dist(int i,int j){ return sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));//不需要将P[i].x p[i].y定义为double 函数类型是double 会自动类型转换 } int find(int x){ int r=x,now; while(x!=fa[x])x=fa[x]; now=r; while(now!=x){ r=fa[now]; fa[now]=x; now=r; } return x;//漏了 } void unio(int x,int y){//union是关键字 int fx=find(x),fy=find(y); if(rank[fx]<rank[fy])fa[fx]=fy; else{ if(rank[fx]==rank[fy])rank[fx]++; fa[fy]=fx; } } int main(){//链式前向型 无法知道起点 int n,m,cnt=0; cin>>m>>n; for(int i=1;i<=n;i++)cin>>p[i].x>>p[i].y,fa[i]=i; /* for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) add(i,j);*/ for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ e[++tot].x=i; e[tot].y=j; e[tot].w=dist(i,j); } // sort(e+1,e+1+tot); sort(e+1,e+1+tot,cmp); for(int i=1;i<=tot;i++){ int fx=find(e[i].x),fy=find(e[i].y); if(fx==fy)continue; unio(fx,fy); ++cnt; ans[cnt]=e[i].w; if(cnt==n-1)break; } printf("%.2f",ans[cnt-m+1]);//ans[n-m] return 0; }