无线通讯网(双倍经验)
无线通讯网
前言
这是一道明显的图论题,主要有两种做法:
最小生成树(Kruskal/Prim)、二分&并查集
本篇题解使用第一种做法解决,如果有不懂最小生成树的可以自己先学习一下(能做到这题应该会)
最小生成树的学习记录写完后,会挂在这里,欢迎各位来踩qwq
引入
PS:题意直接点击链接即可,还是很简明易懂的,就不多赘述
读完题没有思路,那就对样例下手吧,见图(几何画板真是个好东西啊)
再将这些距离从小到大排个序(因为要求最短距离嘛):
200 < 212.13 < 300 < 474.34 < 500 < 667.08
很明显,要使问题中的无线电收发器的传输距离最小,那么667.08
这个距离肯定要避免:所以两个超级电话就安在A、D点,剩下B、C两点,而答案很明显就是212.13
思路推导
- 将样例转换为一般性问题讨论:
因为题目已经说明任意两点只要直接或间接连接即可,所以我们不需要p条边构造完全图,而只需要p-1
条边构造为一棵树
上面s个超级电话之间肯定有(s-1)
条边,所以我们还只剩下(p-1-(s-1))=(p-s)
条边需要构造
- 转换样例推导出思路:
我们就可以直接构造最小生成树了呀!构造的最小生成树包含(p-s)
条边,剩下的(s-1)
条边直接留给超级电话即可
代码Code
明确了思路,这题的代码也就是小问题了,建议大家可以先自己编写啊
现在给出AC代码(使用Kruskal求解最小生成树):
#include <bits/stdc++.h>
using namespace std;
double ans;
int s,p,sx,sy,tot,now,x[2010],y[2010],fa[2010];
struct node {
int u,v;
double w;
} e[2010];
inline bool cmp(node xx,node yy) {
return xx.w<yy.w;
}
inline int find(int x) {
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int main() {
scanf("%d%d",&s,&p);
for(register int i=1;i<=p;i++) {
fa[i]=i;
scanf("%d%d",&x[i],&y[i]);
for(register int j=1;j<i;j++) { //计算每两个点之间的距离(也可以单独开两层循环处理)
e[++tot].u=i;
e[tot].v=j;
e[tot].w=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
}
sort(e+1,e+1+tot,cmp); //Kruskal求解最小生成树
for(register int i=1;i<=tot;i++) {
int a=find(e[i].u);
int b=find(e[i].v);
if(a!=b) {
fa[a]=b;
ans=e[i].w; //因为已经排过序,所以直接更新即可
now++;
}
if(now==p-s) break; //如分析,最小生成树只包含p-s条边,剩余的s-1条边使用超级电话解决
}
printf("%.2lf",ans); //题目要求,保留两位小数输出
return 0;
}
双倍经验
这道题只有判断最小生成树构造完成的判断与无线通讯网不同
简单描述一下思路:给定k个部落,相当于可以少连 \(k-1\) 条边,和卫星电话是差不多的
但是在查找答案时,应该多判断一条边,因为要求是不同部落间的最短距离最大
所以判断改为:if(now==n-k+1) break;
From:Eleven谦