相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米。当然,为了节省资金,只要求实现任意2个小岛之间有路通即可。其中桥的价格为 100元/米。
Input输入包括多组数据。输入首先包括一个整数T(T <= 200),代表有T组数据。
每组数据首先是一个整数C(C <= 100),代表小岛的个数,接下来是C组坐标,代表每个小岛的坐标,这些坐标都是 0 <= x, y <= 1000的整数。
Output每组输入数据输出一行,代表建桥的最小花费,结果保留一位小数。如果无法实现工程以达到全部畅通,输出”oh!”.Sample Input
2
2
10 10
20 20
3
1 1
2 2
1000 1000
Sample Output
1414.2
oh!
题目大意 :给你m个小镇的坐标,求能把它们连通起来的最小路径。
题目分析 :这算一个正正试试的最小生成树的题目了(即kruskal算法),之前写过一道题(还是畅通工程--题目来源),它是直接给了你两个小镇的距离并要求你连接起来,那是点集(变相的边集)来求的,因为它告诉了你
的起点和终点,同时告诉你两点间的权重。这样说来,那道题不过是最小生成数的缩小版。而今天才是真真正正的kruskal算法。
kruskal算法:包括三部分,起点位置(start),终点位置(end),权重(路的距离instance).其余的用并查集的知识即可。
题目收获 :kruskal的正是理解和掌握。
AC代码 :
#include <stdio.h> #include <iostream> #include <algorithm> #include <cstring> #include <math.h> #define maxn 100+5 using namespace std; int per[maxn]; double x[maxn],y[maxn]; struct node { int Start,End; double instance; node():Start(0),End(0),instance(0){} }; bool cmp(node x,node y) { return x.instance<y.instance; } void init() { for(int i=0;i<=maxn;i++) per[i]=i; } int FindSet(int x) { if(x!=per[x]) per[x]=FindSet(per[x]); return per[x]; } bool UnionSet(int x,int y) { int a=FindSet(x); int b=FindSet(y); if(a!=b) per[a]=b; else return false; return true; } void doit() { init(); memset(x,0,sizeof(x)); memset(y,0,sizeof(y)); } int main() { int T; cin >> T; while(T--) { doit(); node bridge[6000]; int a;cin >> a; for(int i=0;i<a;i++) cin >> x[i] >> y[i]; int k=0; for(int i=0;i<a;i++) for(int j=i+1;j<a;j++) { double way=sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); if(way<10.0 || way>1000.0) continue; bridge[k].Start=i; bridge[k].End=j; bridge[k].instance=way; k++; } sort(bridge,bridge+k,cmp); double sum=0; for(int i=0;i<k;i++) { if(UnionSet(bridge[i].Start,bridge[i].End)) sum+=bridge[i].instance; } int cut=0; for(int i=0;i<a;i++) if(i==per[i]) cut++; if(cut==1) printf("%.1f ",sum*100.0); else printf("oh! "); } return 0; }