最小生成树(蒟蒻)

最小生成树

模板:修建道路
题面:
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位实型变量(double)

include<bits/stdc++.h>
using namespace std;

const int shuzu = 2100;
int n,m,x[shuzu],y[shuzu],a[shuzu][shuzu];
bool f[shuzu];//记录某点是否在树中 
double ans,d[shuzu][shuzu],dis[shuzu];//dis为点到树最短距离,d为各点间距离; 

void init(){
    cin>>n>>m;
    int q,w;
    //for(int i = 0;i <= n;i++) for(int k = 0;k <= n;k++) d[i][k] = d[k][i] = 0;
    for(int i = 0;i <= n;i++) dis[i] = 0x3f3f3f3f;
    for(int i = 1;i <= n;i++){
        scanf("%d%d",&x[i],&y[i]);
    }
    for(int i = 1;i <= n;i++){
        for(int k = 1;k <= n;k++){
            d[i][k] = d[k][i] = sqrt(pow(x[i] - x[k],2) + pow(y[i] - y[k],2));
            //cout<<d[i][k]<<" ";
            //计算各点之间的距离
        }
    }
    for(int i = 1;i <= m;i++){
        cin>>q>>w;
        d[q][w] = d[w][q] = 0;//有道路相连,加入树
    }
}

void work(){
    int w = 0;
    for(int i = 2;i <= n;i++){
        dis[i] = d[1][i];
    }
    f[1] = 1;
    for(int i = 1;i <= n;i++){
        for(int k = 1;k <= n;k++){
            if(d[i][k] < dis[i] && f[k]) dis[i] = d[i][k];
        }//各点和树最短路径 
    }
    for(int i = 2;i <= n;i++){
        double minn = 1.0 * 0x3f3f3f3f;
        int p;
        for(int i = 2;i <= n;i++){
            if(dis[i] < minn && !f[i]) minn = dis[i],p = i;
        }//距离树最近的点 
        f[p] = 1;//加入树 
        w--;//如果所有点都加入了树,退出 
        ans += dis[p];
        //cout<<dis[p]<<" ";
        for(int i = 1;i <= n;i++){
            if(dis[i] > d[p][i] && !f[i]) dis[i] = d[p][i];
        }//用加入树的点去更新其他点和树的最短距离 
    }
    printf("%.2lf",ans);
}

int main(){
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值