克鲁斯卡尔算法:从给出的边中 依次选取最短的边 将最短边对应的点 存到一个集合中 直到没有边可供选择。
如hdu1162
,先将所有的边存在dd结构体数组中(此时,所有的点构成了一个集合 笔者称之为 全集),再对边按照升序排列,将最短的边所对的两个点加入到另一个一个集合中(笔者称该集合为 结果集),再从剩下的边中选出最短边,判断(利用并查集)最短边对应的点是否已在结果集中,若不在 则将其加入结果集,若在则重复“从剩下集合中选择最短边”这一操作 继续判断对应点 是否在结果集中 ,如此循环 直到所有点都在结果集中 操作即完成。
点击打开链接HDU 1162;
分析:最小生成树
详解见代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int father[111111];
double s;
struct ssss//坐标点
{
double a,b;
} ss[111];
struct dddd//存两个坐标点 及对应的距离;
{
int a,b;
double x;
} dd[5000];
int Find(int a)//并查集 判断最短边对应的点是否在 结果集 中
{
return a==father[a]?a:father[a]=Find(father[a]);
}
void Union(int i)//利用并查集判断 最短边对应的点是否能加入到结果集中
{
int a=Find(dd[i].a),b=Find(dd[i].b);
if(a!=b)father[a]=b,s+=dd[i].x; //只有没有连通的才能进行这一步,所以每次都是需要连通的最短距离
//若相等,则说明选出的集合众已有这两点。
}
bool cmp(const dddd &a,const dddd &b)
{
return a.x<b.x;
}
int main (void)
{
int n,i,j,k,l;
while(cin>>n)
{
for(i=0; i<n; i++)
cin>>ss[i].a>>ss[i].b;
for(i=0; i<111111; i++)father[i]=i; //初始化
for(i=l=0; i<n; i++)//从第一点出发 存下任意两点之间的距离与对应点坐标。
for(j=i+1; j<n; j++)
{
dd[l].a=i,dd[l].b=j;
double x=ss[i].a-ss[j].a,y=ss[i].b-ss[j].b;
dd[l++].x=sqrt(x*x+y*y);
}
sort(dd,dd+l,cmp);//将所有距离进行升序排序
for(i=0,s=0; i<l; i++)//归并,如果两点不在同一个集合 则放入集合
Union(i); //并起来
printf("%.2f\n",s);
}
return 0;
}