HDOJ1875畅通工程--克鲁斯卡尔算法与查并集探骊

Problem Description
相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现。现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组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!
解题思路:
很明显这道题是赤裸裸的求最小生成树问题,而且生成的图是稀疏图,妥妥的克鲁斯卡尔算法。判断两点之间是否有连线的条件就是距离是否在10到1000之间。设置两个数据结构,一个是点的Node,还有一个是边的,
Edge对象的start和end代表的是点的标号(1~N)。一条线段有两个端点,通过Find函数判断这两个端点是否在一个集合里面,这种方法叫做查并集,方法大同小异:首先,设置并初始化一个Father函数,Father[i]=j说明i节点的下一个节点是j节点。如果Father[i]=i说明i节点是这个集合的代表节点。如果Find(Edge[i].start)=Find(Edge[i].end)说明如果加上Edge[i]这条边就构成了一个回路。由此我总结了克鲁斯卡尔算法的步骤:
step1:用结构体规定边的数据结构
step2:按照边的代价将边从小到大排序(代价可能是价钱、长度)
step3: 用查并集的方法判断边的两个端点是否在一个集合里面,若不是,将start所在集合的代表元素设置为end所在集合代表元素的下一个节点,每添加一条边,计数器加一
step4: 如果计数器的值小于N-1,说明有点是不可达的。
OK,撒刷撒花~完美!!!
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int Father[105];
typedef struct _Node//点
{
	int x, y;
}_Node;
_Node Node[105];
typedef struct _Edge//边
{
	int start;
	int end;
	double length;
}_Edge;
_Edge Edge[100005];
bool cmp(_Edge a, _Edge b)
{
	return a.length < b.length;
}
double get_d(_Node a, _Node b)
{
	return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y,2));
}
int Find(int x)
{
	while (Father[x] != x)
	{
		x = Father[x];
	}
	return x;
}
void Union(int x, int y)
{
	int i = Find(x);
	int j = Find(y);
	if (i != j) { Father[i] = j; }
}
int main()
{
	int T;
	int C;
    double distance;
	cin >> T;
	while (T--)
	{
		cin >> C;
		int total = C - 1;//如果能够成最小生成树,会有total条边
		double sum = 0.0;
		for (int i = 1; i <= C; i++)
		{
			cin >> Node[i].x >> Node[i].y;    
		}
		int k = 1;
		for (int i = 1; i <= C; i++)
		{
			for (int j = i + 1; j <= C; j++)
			{
				distance = get_d(Node[i], Node[j]);
				if (distance >= 10 && distance <= 1000)
				{
					Edge[k].start = i;
					Edge[k].end = j;
					Edge[k++].length = get_d(Node[i], Node[j]);
				}
			}
		}
		sort(Edge + 1, Edge + k + 1, cmp);//将Edge矩阵按照length从小到大的顺序排列
		for (int i = 1; i <= C; i++)//初始化并查集
		{
			Father[i] = i;
		}
		for (int i = 1; i <= k; i++)
		{
			if (Find(Edge[i].start) != Find(Edge[i].end))
			{
				Union(Edge[i].start, Edge[i].end);
				sum += Edge[i].length;
				total--;
			}
		}
		sum *= 100.0;
		if (total != 0) {
			cout << "oh!" << endl;
		}
		else { printf("%.1f\n", sum); }
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值