Description
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 preexistingM 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.
Input
* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Two space-separated integers: Xi andYi
* Lines N+2..N+M+2: Two space-separated integers: i and j, indicating that there is already a road connecting the farmi and farmj.
Output
* 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.
Sample Input
4 1 1 1 3 1 2 3 4 3 1 4
Sample Output
4.00
坐标类的最短路;
基础题。同时再总结一下Prim算法。
选取一个根节点,每次选取与树距离最短的一条路加入。
此题利用邻接矩阵。
在这总结一下最短路Prim与Kruskal的区别:
Prim适合于邻接矩阵,而Kruskal更适合于邻接表。
同时得出一个结论,Prim算法比较适合求稠密图的最小生成树,Kruskal算法比较适合求稀疏图的最小生成树。
在做题时要注意如何选择Prim与Kruskal。
当点少边多时,如1000个点500000条边时,这种变态数据,Prim就要开1000*1000的数组;
而Kruskal只需开一个500000数组。
当点多边少时如500000个点,1000条边,这主要是为了限制内存。Prim就绝对没戏,Kruskal就笑了。
同时,在满足空间的情况下,一般数据的Prim的时间是能完全碾压Kruskal的,大概在20倍左右。此时会有,在变态空间数据选择Kruskal时会超时,而Prim优化内存这不能搞,这时就得Kruskal优化时间了。即在普通Kruskal搜索选择最小边时,做文章,由于每次都得重复搜素最小边,不如一开始将其排序,改进后变为Kruskal+qsort,这时时间就能与Prim相差无几。事实上我们用kruskal一般都是先排序的。所以空间大的时候,和其他情况都可以用到kruskal。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=1002;
const double maxm=2000000;
bool visit[maxn];
int n;
double sum;
double x[maxn],y[maxn],lowcost[maxn],weight[maxn][maxn];
double dist(double x1,double y1,double x2,double y2)
{
return sqrt(pow(x2-x1,2)+pow(y2-y1,2));
}
void Prim(int u)
{
int i,j,k;
double min;
for(i=1;i<=n;i++)
if(i!=u)
lowcost[i]=weight[1][i]; //lowcost取1节点到其他节点的距离。
visit[1]=true;
for(i=1;i<n;i++) //注意!最小生成树有n-1条边
{
min=maxm;
for(j=1;j<=n;j++)
if(lowcost[j]<min && !visit[j])
{
min=lowcost[j];
k=j; //记录哪个点与当前最小生成树的距离最小
}
sum+=min;
visit[k]=true;
for(j=1;j<=n;j++) //给在保留先前的路且在k上寻找较较小的路赋给lowcost
if(lowcost[j]>weight[k][j] && !visit[j])//能是lowcost始终为树外最小的n条路。
lowcost[j]=weight[k][j];
}
}
int main()
{
int i,j,m,a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=1;i<=n;i++) //农场从1开始标号
scanf("%lf%lf",&x[i],&y[i]);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
weight[i][j]=maxm;
for(i=1;i<n;i++) //邻接矩阵
for(j=i+1;j<=n;j++)
{
weight[i][j]=dist(x[i],y[i],x[j],y[j]);
weight[j][i]=weight[i][j];
}
memset(visit,0,sizeof(visit));
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b); //这些dist清为零
weight[a][b]=0;
weight[b][a]=0;
}
sum=0;
Prim(1); //以第一个结点为起点生成最小生成树
printf("%.2lf\n",sum);
}
return 0;
}