Cpp环境【BZOJ1626】【Usaco2007 Dec】【Vijos 1693】 修建道路

【问题描述】         

  Farmer John最近得到了一些新的农场,他想新修一些道路使得他的所有农场可以经过原有的或是新修的道路互达(也就是说,从任一个农场都可以经过一些首尾相连道路到达剩下的所有农场)。有些农场之间原本就有道路相连。

  所有N个农场(用1..N顺次编号)在地图上都表示为坐标为(X_i,Y_i)的点(),两个农场间道路的长度自然就是代表它们的点之间的距离。现在FJ也告诉了你农场间原有的M条路分别连接了哪两个农场,他希望你计算一下,为了使得所有农场连通,他所需建造道路的最小总长是多少。      

【输入格式】         

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

  第2..N+1行: 第i+1行为2个用空格隔开的整数:X_i、Y_i

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

          

【输出格式】         

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

          

【输入样例】           

4 1

1 1

3 1

2 3

4 3

1 4

【输出样例】         

4.00

          

【数据范围】          

1<=N<= 1000  1<=M<=1,000

0<=X_i<=1000000;0<=Y_i<=1000000

          

【样例解释】         

  FJ一共有4个坐标分别为(1,1),(3,1),(2,3),(4,3)的农场。农场1和农场4之间原本就有道路相连。FJ选择在农场1和农场2间建一条长度为2.00的道路,在农场3和农场4间建一条长度为2.00的道路。这样,所建道路的总长为4.00,并且这是所有方案中道路总长最小的一种。

【思路梳理】

       不用去怀疑:这个题让我们求一棵最小生成树,整个地图看做一张二维表,然后给出的是每一个农场即顶点的坐标。我们不难想出:对于任意两个顶点,我们都可以直接修一条路,这条路的权值就是两个顶点间的欧几里得距离(d=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)))。按照Prim算法或者Kruskal算法求出最小生成树的权值和即可。需要注意的是,有的农场之间已经存在了一条或多条路径,对算法形成了一定的干扰。本题提供适用性更广的Kruskal算法思路和代码。

        解答思路大致有两种。一种是提前先将fa数组初始化,对于每一条已经存在的边先在Kruskal算法以前就将其加入到空边图中,然后再按照Kruskal算法的套路进行。另外一种是仍然将这些边加入到边集数组中但是把权值设为0。不需要考虑已经存在原有路径的a,b两个端点又向边集数组加入了一条连接这两个点的路径,因为进行sort排序时,我们按照的是权值由小到大排序,权值为0的会优先加入到空边图当中。给出的代码是第二种思路。

    值得一提的是,对于每一条边的横纵坐标都应该使用double或者longlong来存储而不能是int,因为后面涉及到求欧几里德距离时会有平方,极端情况下会出现10^12,超过了int上界,所以推荐直接用double存储(或者用long long转存一次)。

     

【CPP代码】

#include<cmath>

#include<cstdio>

#include<vector>

#include<iostream>

#include<algorithm>

#define maxn 1005

#define maxm 1005

using namespace std;

int n,m,fa[maxn];

double x[maxn],y[maxn];

struct edge

{

    int u,v;

    double w;

};

vector<edge>E;

int find(int x)

{

    if(fa[x]==x)    return x;

    int root=find(fa[x]);

    fa[x]=root;

    return root;

}

void Union(int a,int b)

{

    fa[find(a)]=find(b);

}

void initial()

{

    for(int i=1;i<=n;i++)    fa[i]=i;

}

void Kruskal()

{

    initial();

    int link=0;

    double ans=0;

    

    for(int i=0;i<E.size();i++)

    {

        int a=E[i].u,b=E[i].v;

        double d=E[i].w;

        

        if(find(a)==find(b))    continue;

        Union(a,b);

        link++;

        ans+=d;

        if(link==n-1)    break;

    }

    printf(“%.2lf”,ans);

}

bool cmp(edge a,edge b)

{

    return a.w+0.000001<b.w;

}

int main()

{

    //freopen(“in.txt”,”r”,stdin);

    cin>>n>>m;

    for(int i=1;i<=n;i++)    scanf(“%lf%lf”,&x[i],&y[i]);

    

    for(int i=1;i<=m;i++)

    {

        int a,b;

        scanf(“%d%d”,&a,&b);

        E.push_back((edge){a,b,0});

    }

    

    

    

    for(int i=1;i<=n;i++)

    for(int j=i+1;j<=n;j++)

    {

        double t=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));

        E.push_back((edge){i,j,t});

    }

    

    sort(E.begin(),E.end(),cmp);

    Kruskal();

    return 0;

}

    原文作者:道路修建问题
    原文地址: https://blog.csdn.net/ArkhamOrginal/article/details/51887929
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞