poj3625 最小生成树 Prim

本博客讨论了Farmer John如何通过构建道路连接多个农场,以实现任意农场间的通行。利用几何坐标与邻接矩阵,通过Prim算法解决最短路径问题,详细解释了算法流程与应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}



 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值