图论5-最小生成树变形
这章要讲的是最小生成树的一些变形,所以在看这篇博客之前建议先学会基础的克鲁斯卡尔版的最小生成树的模板。
这次的题目是 POJ2349 Arctic Network
题目大意:一个村子要建成通讯系统,也就是任意两家的能被连接,共有两种通讯方式,卫星电话和通讯机。持有
卫星电话的两家不管隔多远都能互相传达信息,但是卫星电话数量有限,剩下的就要考通讯机了。通讯机是统一购
买的,所以每台通讯机的传播范围都为d。求让任意两家都能通讯(直接或间接)要买的通讯机传播范围最少是几?
思路解析:因为让所有人都能互相通讯的意思是让所有结点都能互相到达,所以就是要求最小生成树,但是有卫星
电话的存在却让这道题不是一个裸的最小生成树。因为卫星电话可以让两个远在天边的人通话,可以看做卫星电话
使这个生成树可以不连通,也就是一座森林。而生成森林和生成树的区别是可以少连边,所以一定比生成树优,但
要在生成树上剪掉哪些边呢?当然是在剪长度越大的边越好啦,所以克鲁斯卡尔中的最后k-1条边可以被剪掉 ,也
就是枚举边到num-k即可。然后我们再梳理一下思路:
思路梳理:
1.读入每个节点的数据。
2.算出每条边及其长度(也就是任意点间的距离)
3.正常克鲁斯卡尔,只不过是取max而非+=,并且num也不是n-1而是n-k。
4.输出答案,记得保留两位小数。
完整代码如下:
#include<bits/stdc++.h> using namespace std; const int NR=505; const int MR=NR*NR; int n,m,k; struct Nd { int x,y; }a[NR];// 存储点的数据 struct Path { int a,b; double dis; }p[MR];// 存储边的数据 double max_(double x,double y) { if(x>y) return x; return y; }//手写double的max bool cmp(Path s,Path t) { return s.dis<t.dis; }//对边排序的cmp double dist(int s,int t) { double x=(a[s].x-a[t].x)*(a[s].x-a[t].x)+(a[s].y-a[t].y)*(a[s].y-a[t].y); return sqrt(x);//dist中必须要是先转成double再取sqrt否则就会返回一个整数 }//算任意两点间距离 int fa[NR];//记录并查集中父亲节点编号 int find(int x) { if(x==fa[x]) return x; return find(fa[x]); }//并查集,克鲁斯卡尔正常操作 int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*f; } int main() { // freopen("1.in","r",stdin); // freopen("1.out","w",stdout); int T=read(); while(T--) { m=0;//初始化 k=read(),n=read(); for(int i=1;i<=n;i++) { a[i].x=read(),a[i].y=read(); fa[i]=i;//初始化 }//读入点的数据 for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { m++;p[m].a=i;p[m].b=j; p[m].dis=dist(i,j);//计算任意两点间的距离 } }//计算边的数据 sort(p+1,p+m+1,cmp); int num=n-k;//有k部卫星电话,所以num=n-k double ans=0; for(int i=1;i<=m;i++) { if(!num) break; int fx=find(p[i].a),fy=find(p[i].b); if(fx!=fy) { num--; fa[fx]=fy; ans=max_(ans,p[i].dis); } }//克鲁斯卡尔正常操作 printf("%.2f\n",ans);//保留两位小数并输出 } return 0; }