【洛谷P2872】道路建设(最小生成树prim/kruskal)

题目地址:https://www.luogu.org/problemnew/show/P2872

题目描述


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

输出格式:

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

输入输出样例

输入样例#1: 

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

输出样例#1: 

4.00

解题思路:


对于已经修建了的边,把其边权设为0 ,优先选,这道题有点像HDOJ1879洛谷P1256的集合

 

ac代码:


prim

#include <iostream>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <fstream>
#define  maxn 1005
typedef long long ll;
const ll inf=1e+18;
using namespace std;
ll n,m,u,v,vis[maxn]={0},edges[maxn][maxn],pre[maxn]={0};
double dis[maxn];
struct Node{
    ll x,y;
}node[maxn];
double getdis(Node a,Node b)
{
    return sqrt((double)(a.x-b.x)*(a.x-b.x)+double(a.y-b.y)*(a.y-b.y));//转成double,防溢出
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    fill(dis,dis+maxn,inf);
    memset(edges,0,sizeof(edges));
    scanf("%lld %lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld %lld",&node[i].x,&node[i].y);
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld %lld",&u,&v);
        edges[u][v]=edges[v][u]=1;//已经修建,优先选择
        if(u==1) dis[v]=0;
        if(v==1) dis[u]=0;
    }
    dis[1]=0;pre[1]=1;
    double ans=0,d;
    for(ll i=0;i<n;i++)
    {
        double min=inf;
        int k=1;
        for(int j=1;j<=n;j++)
        {
            if(dis[j]<min&&!vis[j])
            {
                min=dis[j];
                k=j;
            }
        }
        vis[k]=1;
        ans+=dis[k];
        for(int j=1;j<=n;j++)
        {
            if(edges[k][j]==1) d=0;//已经修建
            else d=getdis(node[k],node[j]);
            if(!vis[j]&&d<dis[j])
                dis[j]=d;
        }
    }
    printf("%.2lf\n",ans);
    return 0;
}

kruskal

#include <iostream>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <fstream>
#define  maxn 1005
typedef long long ll;
const ll inf=1e+18;
using namespace std;
ll f[maxn],n,m,a,b,num=0;
double ans=0;
ll find_father(ll x)
{
    return f[x]==x?x:f[x]=find_father(f[x]);
}
struct Node{
    ll x,y;
}node[maxn];
struct Edges
{
    ll u,v;
    double val;//u、v之间的边权val
    friend bool operator <(Edges a,Edges b)
    {
        return a.val<b.val;
    }
}edges[maxn*maxn/2+10];//注意数组大小,边数

int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%lld %lld",&n,&m);
    for(ll i=1;i<=n;i++)
    {
        f[i]=i;//初始化
        scanf("%lld %lld",&node[i].x,&node[i].y);
    }
    for(ll i=1;i<=n;i++)
    {
        for(ll j=i+1;j<=n;j++)//j=i+1
        {
            double d=sqrt(double(node[i].x-node[j].x)*(node[i].x-node[j].x)+double(node[i].y-node[j].y)*(node[i].y-node[j].y));
            edges[++num].u=i;
            edges[num].v=j;
            edges[num].val=d;
        }
    }
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld %lld",&a,&b);
        edges[++num].u=a;
        edges[num].v=b;
        edges[num].val=0;//虽然多加了边数,但是在生成最小生成树的时候优先选,所以原来的val值实际上是不被加到最终结果里的
    }
    sort(edges+1,edges+1+num);
    for(ll i=1;i<=num;i++)
    {
        ll x=find_father(edges[i].u);
        ll y=find_father(edges[i].v);
        if(x!=y)
        {
            f[x]=y;//合并
            ans+=edges[i].val;
        }
    }
    printf("%.2lf\n",ans);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值