hdu 1875 畅通工程再续

畅通工程再续

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 33067    Accepted Submission(s): 10887


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
 
 
2210 1020 2031 12 21000 1000
 

Sample Output
 
 
1414.2oh!
 

Author
8600
 

Source

最小生成树问题,和1233题类似,不同的是边的大小需要自己算出来

方法一:kruskal算法

算法过程:

1.将图各边按照权值进行排序

2.将图遍历一次,找出权值最小的边,(条件:此次找出的边不能和已加入最小生成

 

树集合的边构成环),若符合条件,则加入最小生成树的集合中。不符合条件则继

 

续遍历图,寻找下一个最小权值的边。

3.递归重复步骤1,直到找出n-1条边为止(设图有n个结点,则最小生成树的边数应

 

为n-1条),算法结束。得到的就是此图的最小生成树。


#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
struct Node{
	int s,e;
	double dis;
}v[10010];
struct land{
	int x,y;
}a[110];
int len,pre[110],n,T;
void init(){
	for(int i = 0;i < 110;i++) pre[i] = i;
	len = 0;
}
int find(int root){
	if(root == pre[root]) return root;
	else return pre[root] = find(pre[root]);
}
bool join(int a,int b){
	int x = find(a);
	int y = find(b);
	if(x != y){
		pre[x] = y;
		return true;//返回该边能加进去 
	}
	return false;//返回该边能加进去 
}
bool cmp(Node &a,Node &b){
	return a.dis < b.dis;
}
void kruskal(){
	double sum = 0;
	for(int i = 0;i < len;i++){
		if(v[i].dis<=1000&&v[i].dis>=10&&join(v[i].s,v[i].e)){//不符合要求的点不能加进去 
			sum+=v[i].dis;
		}
	}
	int cnt = 0;
	for(int i = 0;i < n;i++){
		if(pre[i] == i) cnt++;
	}
	if(cnt > 1){
		printf("oh!\n");
	}else{
		printf("%.1lf\n",sum*100);
	}
}
int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d",&T);
	while(T--){
		init();
		scanf("%d",&n);
		for(int i = 0;i < n;i++){
			scanf("%d%d",&a[i].x,&a[i].y);
		}
		for(int i = 0;i < n;i++){//添加所有的的边 
			for(int j = i+1;j<n;j++){
				v[len].e = j;
				v[len].s = i;
				v[len++].dis = sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
			}
		}
		sort(v,v+len,cmp);//按边的长度从小到大排序 
		kruskal();
	}
	return 0;
}

方法二:

普利姆(Prime)算法(只与顶点相关)

 

算法描述:

普利姆算法求最小生成树时候,和边数无关,只和定点的数量相关,所以适合求稠

 

密网的最小生成树,时间复杂度为O(n*n)。

算法过程:

1.将一个图的顶点分为两部分,一部分是最小生成树中的结点(A集合),另一部分

 

是未处理的结点(B集合)。

2.首先选择一个结点,将这个结点加入A中,然后,对集合A中的顶点遍历,找出A中

 

顶点关联的边权值最小的那个(设为v),将此顶点从B中删除,加入集合A中。

3.递归重复步骤2,直到B集合中的结点为空,结束此过程。

4.A集合中的结点就是由Prime算法得到的最小生成树的结点,依照步骤2的结点连接

 

这些顶点,得到的就是这个图的最小生成树。


#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int T,n;
#define INF 999999
double map[110][110],dis[110];
bool use[110];
struct node{
	int x,y;
}a[110];
void prim(){
	double sum = 0;
	use[0] = true;
	double MIN;
	int temp;
	for(int i = 1;i < n;i++) dis[i] = map[0][i];
	for(int i = 1;i < n;i++){
		temp = i,MIN = INF;
		for(int j = 1;j < n;j++){
			if(!use[j]&&MIN > dis[j]&&dis[j]>=10&&dis[j]<=1000){
				MIN = dis[j];
				temp = j;
			}
		}
		if(temp == i){
			if(!use[i]&&dis[i]>=10&&dis[i]<=1000){
				sum+=dis[i];
				use[i] = true;
			}else{
				if(!use[i]){
					printf("oh!\n");
					return ;
				}
			}
		}else{
			use[temp] = true;
			sum += MIN;
		}
		for(int j = 1;j < n;j++){
			if(!use[j]&&dis[j] > map[j][temp]&&map[j][temp]>=10&&map[j][temp]<=1000){
				dis[j] = map[j][temp];
			}
		}
	}
	int cnt = 0;
	for(int i = 0;i < n;i++){
		if(!use[i]) cnt++;
	}
	if(cnt > 0){
		printf("oh!\n");
	}else{
		printf("%.1lf\n",sum*100);
	}
}
int main(){
//	freopen("input.txt","r",stdin);
	scanf("%d",&T);
	while(T--){
		memset(use,false,sizeof(use));
		scanf("%d",&n);
		for(int i = 0;i < n;i++){
			scanf("%d%d",&a[i].x,&a[i].y);
	//		printf("%d %d\n",a[i].x,a[i].y);
		}
		for(int i = 0;i < n;i++){
			map[i][i] = 0;
			for(int j = i+1;j < n;j++){
				map[i][j] = map[j][i] = sqrt(1.0*(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
				if(map[i][j]<10||map[i][j]>1000){
					map[i][j] = map[j][i] = INF;
				}
			//	cout<<map[i][j]<<" ";
			}
		}
		prim();
	}
	return 0;
}


 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值