分析
二分做法粗暴无脑,挺好的。
大体思路是:
- 预处理出边权
- 二分出一个连边的上界,对边权进行排序
- 对于二分的
check
,如果联通块的数量 $\leqslant k$ 那么满足check()
,否则不满足。
感谢 @斗牛犬 指出问题 orz
#include<bits/stdc++.h>
using namespace std;
const int N=505;
const double eps=1e-6;
int n,k;
int f[N];
struct Ver{
double x,y;
}v[N];
struct Edge{
int u,v;
double w;
bool operator<(const Edge &o)const{
return w<o.w;
}
}e[N*N*2];
double cal(int a,int b){
return sqrt((v[a].x-v[b].x)*(v[a].x-v[b].x)+(v[a].y-v[b].y)*(v[a].y-v[b].y));
}
int tot;
void get_edges(){
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++){
e[++tot]={i,j,cal(i,j)};
}
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool check(double d){
for(int i=1;i<=n;i++) f[i]=i;
int idx=tot;
for(int i=1;i<=tot;i++){
double w=e[i].w;
if(w>d+eps){
idx=i-1;
break;
}
}
int res=0;
for(int i=1;i<=idx;i++){
int u=e[i].u, v=e[i].v;
int pu=find(u), pv=find(v);
if(pu!=pv){
res++;
f[pu]=pv;
}
}
return n-res<=k;
}
int main(){
cin>>n>>k;
if(!k) k=1;
for(int i=1;i<=n;i++){
double x,y; cin>>x>>y;
v[i]={x,y};
}
get_edges();
sort(e+1,e+1+tot);
double l=0,r=e[tot].w;
while(l+eps<r){
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.2lf",l);
return 0;
}
楼主的代码加个特判,不然会wa,原因应该是k==0的时候,连通块个数应该为1个
thx
数据加强了,WA了,这种kruskal方法不能统计连通块数量
应该是能统计连通块的数量吧,得特判k==0,因为k==0时跟k==1没什么区别
为什么二分+并查集要比kruskal慢很多?
并查集做法就是 kruskal 吧,这个做法相当于是加了一个二分也就是复杂度多了 log
从前往后一个个搜不更慢吗
博主这一题不用二分的最小生成树与最短路不同最小生成树一定是n-1条边因此无论你的d如何变选的边依旧是那几条不如直接求第k-1大的那条边
是的,但是二分在思维上简单一点qwq