[Usaco2007 Dec]Building Roads 修建道路

题目描述

Farmer John最近得到了一些新的农场,他想新修一些道路使得他的所有农场可以经过原有的或是新修的道路互达(也就是说,从任一个农场都可以经过一些首尾相连道路到达剩下的所有农场)。有些农场之间原本就有道路相连。 所有N(1 <= N <= 1,000)个农场(用1..N顺次编号)在地图上都表示为坐标为(X_i, Y_i)的点(0 <= X_i <= 1,000,000;0 <= Y_i <= 1,000,000),两个农场间道路的长度自然就是代表它们的点之间的距离。现在Farmer John也告诉了你农场间原有的M(1 <= M <= 1,000)条路分别连接了哪两个农场,他希望你计算一下,为了使得所有农场连通,他所需建造道路的最小总长是多少。

输入

* 第1行: 2个用空格隔开的整数:N 和 M

 * 第2..N+1行: 第i+1行为2个用空格隔开的整数:X_i、Y_i * 第N+2..N+M+2行: 每行用2个以空格隔开的整数i、j描述了一条已有的道路, 这条道路连接了农场i和农场j

输出

* 第1行: 输出使所有农场连通所需建设道路的最小总长,保留2位小数,不必做 任何额外的取整操作。为了避免精度误差,计算农场间距离及答案时 请使用64位实型变量

一个最小生成树裸题。
开始的时候,那些已经有的边就不需要再加入了,之后加入每个点到其他点的连线。跑kruskal就可以了。
另外发现了一个不错的写法:可以不用统计边的数量。只要把所有边都判断一次就可以了。如下:
for(int i=1;i<=cnt;i++)
   {
     if(find(L[i].a)!=find(L[i].b))
     fa[find(L[i].a)]=find(L[i].b),ans+=L[i].v;
   }
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1002;
int n,m,cnt,fa[N],tot;
bool use[N];
double ans;
struct point
{
  int x,y;
}p[N];
double cross(point a,point b)
{
	long long aa=(long long)(a.x-b.x),bb=(long long)(a.y-b.y);//!!不然会爆int 
   return sqrt(aa*aa+bb*bb);
}
struct line
{
   int a,b;
   double v;
}L[N*N];
bool cmp(line a,line b)
{
   return a.v<b.v;
}
int find(int x)
{
  if(fa[x]==x)return x;
  return fa[x]=find(fa[x]);
}
void add(int x,int y)
{
  L[++cnt].a=x,L[cnt].b=y;
  L[cnt].v=cross(p[x],p[y]);	
}
void pre()
{
  cin>>n>>m;
  for(int i=1;i<=n;i++)cin>>p[i].x>>p[i].y,fa[i]=i;
  int tx,ty;
  for(int i=1;i<=m;i++)cin>>tx>>ty,fa[find(tx)]=find(ty); 
  for(int i=1;i<=n;i++)
  for(int j=1;j<=n;j++)
  {
  	 if(i==j)continue;
     if(find(i)!=find(j))
     add(i,j);
  }
 
  sort(L+1,L+cnt+1,cmp);
}
void kruskal()
{
  	for(int i=1;i<=cnt;i++)
  	{
  	  if(find(L[i].a)!=find(L[i].b))
  	  fa[find(L[i].a)]=find(L[i].b),ans+=L[i].v;
  	} 	
	printf("%.2f",ans);	
}
int main()
{
   pre();
   kruskal();
return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值