HDU 1875 畅通工程再续

畅通工程再续

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


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!
 

Author
8600
 

Source
 
 
 
思路:
 
            这道题和之前做的给出两点以及两点之间的距离的题有一点不同,就是要将点的坐标转化成两个点及这两个点之间的距离,一起放到结构体中,然后就和原来的最小生成树的模板就一目一样了,具体代码如下:
 
 
代码:
 
//要注意:每一个点都有可能有关系,所以不能从第一个点开始找与第一个点之间的距离最短的点,然后 
//保存在一个结构体中,然后找与第二个点之间最短的距离的点,存到结构体中,这样最终只形成
//两个点之间的关系,而与其他的点之间没有关系了!如果你加上判断那两个点之间已经连过,你去用第二个点 
//连其他的点,也不行,万一人家再用第一个去连其他的而第二个就只和第一个相连,那不是就会出错了
//所以,这种做法完全错误!(虽然想了好长时间)!这还是要往模板上靠,将点的坐标转化成距离和点对应 
// 的代号,这样就是模板,就很容易了! 
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
int n,m;
int pre[105];
struct node
{
	int x,y;
}a[105];
struct Q
{
	int u,v;
	double w;
}p[10005];

void init()
{
	for(int i=1;i<=n;i++)
		pre[i]=i;
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y);
}
int cmp(Q a,Q b)
{
	return a.w<b.w;
}
int find(int x)
{
	int r=x;
	while(r!=pre[r])
	{
		r=pre[r];
	}
	int i,j;
	i=x;
    while(i!=r)
    {
    	j=pre[i];
    	pre[i]=r;
    	i=j;
    }
    return r;
}
int join(int x,int y)
{
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy)
	{
		pre[fx]=fy;
		return 1;
	}
	return 0;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		init();
		int t=0;
		double d;
		for(int i = 1;i <= n - 1; i++)//将对应的点转化成为两个点,和两个点之间的距离放到结构体中 
			for(int j = i + 1;j <= n; j++)
			{
				d=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));
				if(d>=10&&d<=1000)//需要满足这个条件才能放进结构体数组中! 
				{
				p[++t].u=i;
				p[t].v=j;
				p[t].w=d;
			    }
	        }
	    sort(p+1,p+t+1,cmp);//按照距离从小到大的顺序排序! 
	    double sum=0;
	    for(int i=1;i<=t;i++)
	    {
	    	if(join(p[i].u,p[i].v))//也就是两点之间最短的距离加到sum上! 
	    	{
	    		sum+=p[i].w;//sum代表是所有满足条件的距离的总和! 
	    	}
	    }
	    int cnt=0;
	    for(int i=1;i<=t;i++)//判断根节点的个数! 
	    {
	    	if(pre[i]==i)
	    		cnt++;
	    }
	    if(cnt!=1)//不是一棵树,就输出oh! 
	    	printf("oh!\n");
	    else//是一颗树就输出需要花的钱! 
	    	printf("%.1lf\n",sum*100);
	}
	return 0;
} 

  方法2:
  
         prim算法:
 
//prim算法!!!!!!!!! 
/*
prim函数的思想就是,将集合外的点拿出来一个放进集合里面,然后算集合外的点与集合内的点之间的距离,
将最短的距离的点放进集合,就这样一直进行,直到所有的点都放进集合为止!(集合外的点与集合内的点之间的
距离的最短值的计算并不复杂,就是将近集合里面一个元素,就将距离更新一下,就是讲每个点到新进到集合
里面的点之间的距离与之前的最短距离进行比较,如果比之前的小,就需要更新!) 
 
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define INF 0xfffffff
int n;
double d[105][105];
int vis[105];
double min;
double dis[105]; 
struct zb
{
	int  x;
	int y;
}a[105];
double dist(int i,int j)
{
	return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)*1.0+(a[i].y-a[j].y)*(a[i].y-a[j].y)*1.0);
}
void init()//初始化各个变量! 
{
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)//初始化d之后,一定要用d数组,而不能用算距离的,因为你用算距离的函数 
	{//不能够筛选在给定范围内的距离! 
		d[i][j]=d[j][i]=dist(i,j);
		if(d[i][j]<10||d[i][j]>1000)
		   d[i][j]=d[j][i]=INF;
	}//注意用d数组,而不能用距离函数,错了n次! 
	for(int i=1;i<=n;i++)//我们假设每个点到第1个点的距离为最小值! 
		dis[i]=d[1][i]; //代表第i个点到第1个点的距离! 
	memset(vis,0,sizeof(vis));//初始化,都没有进集合! 
	     vis[1]=1;//第一个点先放到集合里面,然后将其他的点与集合里面的点求距离,将最小的距离的点放进集合! 
}

int main()
{
	int T,t;
	double min;//注意是double型的! 
	scanf("%d",&T);
	while(T--)
	{
		int k=1;
		double s=0;
		scanf("%d",&n);
		init();
		t=0;
		for(int i=1;i<n;i++)//找n-1个点 
		{
			min=INF;
			for(int j=1;j<=n;j++)//找最小的距离 
			{
				if(!vis[j]&&(dis[j]<min))//第j个点到集合的距离的最小值! 
				{
					min=dis[j];
					k=j;//保留最近的点到集合的距离的下标! 
				}
			}
			if(min==INF)//如果所有的未标记的点到集合的距离为无穷大,(也就是把距离近的点都进到集合里面之后,剩下距离较大的点了,) 
			{//到集合的距离为无穷大,也就是那些不满足条件的点不能进集合,最终肯定不能形成一棵树! 
				
				break;
			}
			t++;
			vis[k]=1;//距离集合最小的点进集合,并进行标记 
			s+=min;//并且将最小的距离加到s上! 
			for(int j=1;j<=n;j++)//对集合外的点到集合的距离进行更新! 
			{
				if(!vis[j]&&(dis[j]>d[j][k]))//如果加到集合里面的点距离集合外面的点的距离更近,就得将小的值赋值给dis[i]! 
					dis[j]=d[j][k];
			}
		}
		if(t==n-1)//判断是否都进集合里面,如果都进集合里面,则输出最小值! 
		{
			printf("%.1lf\n",s*100);
		}
		else
		printf("oh!\n");
	} 
	return 0;
} 

  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值