P2872 [USACO07DEC]Building Roads S

# [USACO07DEC]Building Roads S

## 题目描述

Farmer John had just acquired several new farms! He wants to connect the farms with roads so that he can travel from any farm to any other farm via a sequence of roads; roads already connect some of the farms.

Each of the N (1 ≤ N ≤ 1,000) farms (conveniently numbered 1..N) is represented by a position (Xi, Yi) on the plane (0 ≤ Xi ≤ 1,000,000; 0 ≤ Yi ≤ 1,000,000). Given the preexisting M roads (1 ≤ M ≤ 1,000) as pairs of connected farms, help Farmer John determine the smallest length of additional roads he must build to connect all his farms.

给定 $n$ 个点的坐标,第 $i$ 个点的坐标为 $(x_i,y_i)$,这 $n$ 个点编号为 $1$ 到 $n$。给定 $m$ 条边,第 $i$ 条边连接第 $u_i$ 个点和第 $v_i$ 个点。现在要求你添加一些边,并且能使得任意一点都可以连通其他所有点。求添加的边的总长度的最小值。

## 输入格式

\* Line 1: Two space-separated integers: N and M

\* Lines 2..N+1: Two space-separated integers: Xi and Yi

\* Lines N+2..N+M+2: Two space-separated integers: i and j, indicating that there is already a road connecting the farm i and farm j.

第一行两个整数 $n,m$ 代表点数与边数。   
接下来 $n$ 行每行两个整数 $x_i,y_i$ 代表第 $i$ 个点的坐标。   
接下来 $m$ 行每行两个整数 $u_i,v_i$ 代表第 $i$ 条边连接第 $u_i$ 个点和第 $v_i$ 个点。

## 输出格式

\* Line 1: Smallest length of additional roads required to connect all farms, printed without rounding to two decimal places. Be sure to calculate distances as 64-bit floating point numbers.

一行一个实数代表添加的边的最小长度,要求保留两位小数,为了避免误差, 请用 $64$ 位实型变量进行计算。

## 样例 #1

### 样例输入 #1

```
4 1
1 1
3 1
2 3
4 3
1 4
```

### 样例输出 #1

```
4.00
```

## 提示

### 数据规模与约定

对于 $100\%$ 的整数,$1 \le n,m \le 1000$,$1 \le x_i,y_i \le 10^6$,$1 \le u_i,v_i \le n$。

### 说明

Translated by 一只书虫仔。

1.这道题是最小生成树问题,可以使用prim,也可以用kruskal算法。

起初我使用的是prim算法,比较暴力的那种prim算法,去依次筛选距离最小生成树最小值,很遗憾只拿了70分,时间超限了,下面是我70分的代码。

#include<stdio.h>
#include<math.h>
#define N 10010
struct node
{
	int x;
	int y;
}vertex[N];
int n,m,f[N];
double dis[N];
int getf(int x)
{
	if(x==f[x]) return x;
	f[x]=getf(f[x]);
}
int merge(int a,int b)
{
	int p,q;
	p=getf(a);
	q=getf(b);
	if(p!=q)
	{
		f[q]=p;
		return 1;
	}
	else return 0;
}
double min(double a,double b)
{
	if(a>b) return b;
	return a;
}
double distance(struct node a,struct node b)
{
	return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}
int fun()
{
	int tree=1,i,j,k,ares;
	double nox=2147483647,sum=0;
//	book[1]=1;//确定第一个点为最小生成树
	while(tree<n)//如果树已经到了所有的点,
	//表明最小生成树已经构建完毕
	{
		//需要刷新最小生成树到其他点的所有距离
		//并且找到最小值
		ares=0;
		for(i=1;i<=n;i++)//找所有在树里面的点
		{
			if(getf(i)==1)//如果是
			{
				ares++;
				for(j=1;j<=n;j++)//刷新其他点
				{
					if(getf(j)!=1)
					{
						dis[j]=min(dis[j],distance(vertex[i],vertex[j]));
					}
				}
			}
		}
		//找距离最小生成树最短的点
		nox=2147483647;
		for(i=1;i<=n;i++)
		{
			if(getf(i)!=1&&nox>dis[i]) 
			{
				nox=dis[i];
				j=i;
			}
		}
		
		sum+=dis[j];
		k=getf(j);
	
		//这个最短的点是否还附带了其他点,把他们纳入最小生成树
		for(i=1;i<=n;i++)
		{
			if(k==getf(i))//如果有
			{
				f[getf(i)]=1;//变成最小生成树
				//tree++;
			}
		}
		
		f[k]=1;
		tree=ares;
	}
//	for(i=1;i<=n;i++)
//		printf("%.2f ",dis[i]);
//	puts("");
	printf("%.2f",sum);
}
int main()
{
	int i,j,u,v;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		f[i]=i,dis[i]=2147483647;
	for(i=1;i<=n;i++)
		scanf("%d%d",&vertex[i].x,&vertex[i].y);
	for(i=0;i<m;i++)
	{
		scanf("%d%d",&u,&v);
		merge(u,v);
	}
	fun();
	return 0;
	
}

但是我又不太会写有关堆优化的prim算法,只是知道堆排序和优先队列的概念。

然后去看了题解,发现kruskal算法也可以实现,于是我又写了kruskal的代码,然后AC了90分。

这里就有一个小重点了:如果节点已经有路径了,我们就将该边路径设置为0,即使后面加上也不会影响答案。在遍历边的时候,我们把已经有路径的俩个节点进行并查集合并,并且tree(存储最小生成树里面已经有几点)也是可以++的(我忘记了,但是还是AC了),最后去遍历边存储到数组里面,我们只需要排序这些边即可,那些已经在最小生成树里面的就不必排序了,会节省很多时间(我就是因为没有这么写,导致最后一个案列TLE了)

C代码如下:

#include<stdio.h>
#include<math.h>
#define N 10010
struct node
{
	int x;
	int y;
}vertex[N];

struct ares
{
	int u,v;
	double w;
}edges[N];

int n,m,f[N];
char book[N][N];

int getf(int x)
{
	if(x==f[x]) return x;
	 return f[x]=getf(f[x]);
}
int merge(int a,int b)
{
	int p,q;
	p=getf(a);
	q=getf(b);
	if(p!=q)
	{
		f[q]=p;
		return 1;
	}
	return 0;
}
double min(double a,double b)
{
	if(a>b) return b;
	return a;
}
double distance(struct node a,struct node b)
{
	return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}

int quicksort(int left,int right)
{
	if(left>=right) return 0;
	int i=left,j=right;
	struct ares t,temp=edges[left];
	while(i<j)
	{
		while(i<j&&temp.w<=edges[j].w) j--;
		while(i<j&&temp.w>=edges[i].w) i++;
		if(i<j)
		{
			t=edges[i];edges[i]=edges[j];edges[j]=t;
		}
	}
	edges[left]=edges[i];
	edges[i]=temp;
	quicksort(left,i-1);
	quicksort(i+1,right);
}
int fun(int k)
{
	int i,j,tree=1;
	double sum=0;
	for(i=0;i<k;i++)
	{
		if(merge(edges[i].u,edges[i].v))
		{
			sum+=edges[i].w;
			tree++;if(tree>n) break;
		}
		
	}
	printf("%.2f",sum);
	return 0;
}
int main()
{
	int i,j,u,v,k=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	{
		f[i]=i;
		scanf("%d%d",&vertex[i].x,&vertex[i].y);
	}
	for(i=0;i<m;i++)
	{
		scanf("%d%d",&u,&v);
		merge(u,v);
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<i;j++)
		{
			edges[k].u=i;
			edges[k].v=j;
			edges[k].w=distance(vertex[i],vertex[j]);
			k++;
		}
	}
	quicksort(0,k-1);
//	for(i=0;i<k;i++)
//		printf("%.2f ",edges[i].w);
	fun(k);
	return 0;
	
}

C++代码如下:

#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
#include<cmath>

using namespace std;

const int N=10010;

struct node
{
	int x;
	int y;
}vertex[N];

struct ares
{
	int u,v;
	double w;
}edges[N];

int n,m,f[N];

int getf(int x)
{
	if(x==f[x]) return x;
	 return f[x]=getf(f[x]);
}
int merge(int a,int b)
{
	int p,q;
	p=getf(a);
	q=getf(b);
	if(p!=q)
	{
		f[q]=p;
		return 1;
	}
	return 0;
}
double distance(node a,node b)
{
	return sqrt((double)(a.x-b.x)*(a.x-b.x)+(double)(a.y-b.y)*(a.y-b.y));
}
bool cmp(ares a,ares b)
{
	return a.w<b.w;
}

int fun(int k)
{
	int i,j,tree=1;
	double sum=0;
	for(i=0;i<k;i++)
	{
		if(merge(edges[i].u,edges[i].v))
		{
			sum+=edges[i].w;
			tree++;
			if(tree>n) break;
		}	
	}
	printf("%.2f",sum);
	return 0;
}
int main()
{
	int i,j,u,v,k=0;
	cin >> n >> m ;
	for(i=1;i<=n;i++)
	{
		f[i]=i;
		cin >> vertex[i].x >> vertex[i].y ;
	}
	for(i=0;i<m;i++)
	{
		cin >> u >> v ;
		merge(u,v);
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<i;j++)
		{
			edges[k].u=i;
			edges[k].v=j;
			edges[k].w=distance(vertex[i],vertex[j]);
			k++;
		}
	}
	sort(vertex,vertex+k-1,cmp);
	fun(k);
	return 0;
	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值