[洛谷]P2872 [USACO07DEC]Building Roads S(最小生成树问题)

关键思路:求出每个点之间的距离并储存(用结构体,以便东西使用kruskal),然后把已经相连点之间的距离变为0,(把这道题看连接每个的最短距离(最小生成树问题)),已经相连的点正常是不用再连的,但是这样会使问题复杂化,所以我们将已经相连的点之间的距离改0,即使被访问到也没关系,因为二者之间的距离看成0.

!!!这样会出现一个问题:边的总数会增加,因为你在把已经相连点之间的距离变为0之前,已经将所有点之间的距离都存储过一遍了,在把已经相连点之间的距离变为0的时候又存储一遍,都是没关系kruskal算法每次都是拿出最短的距离,自然距离为0的一定被先选上,当条数到n-1的时候自然退出。

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 5000100;
int n, m, cnt, fa[N], sum;
double ans;
struct Node {
    int x, y;
}E[N];

struct node {
    int from, to;
    double w;
}e[N];

void add(int x, int y, double z) {
    e[++cnt].from =x;
    e[cnt].to = y;
    e[cnt].w = z;
}

double jl(int x, int y) {//距离
    return (double)(sqrt((double)(E[x].x - E[y].x) * (E[x].x - E[y].x) + (double)(E[x].y - E[y].y) * (E[x].y - E[y].y)));
}

bool cmp(node x, node y) {//按照w来排序,标准的kruskal的模板)
    if(x.w == y.w) return x.from < y.from;
    return x.w < y.w;
}

int find(int x) {//并查集(标准的kruskal的模板) 
    if(x==fa[x]) return x;
    else
    {
        fa[x]=find(fa[x]);
        return fa[x];
    }
}
void kruskal()//标准模板 
{
        for(int i = 1; i <= cnt; i++) {//标准的kruskal //i<=cnt
        int fx = find(e[i].from), fy = find(e[i].to);
        if(fx != fy) {
            fa[fx] = fy;
            sum++;
            ans += e[i].w;
        }
        if(sum == n - 1) break;
    }
}
int main() {
    cin>>n>>m;
    for(int i = 1; i <= n; i++) cin>>E[i].x>>E[i].y;//输入 
        
    for(int i = 1; i <= n; i++) fa[i] = i;//初始化父节点
     
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            double z = jl(i, j);
            add(i, j, z);//存入点i到点j的距离z 
        }
    }
    
    for(int i = 1; i <= m; i++) {//********************************************
        int x ,y;
     cin>>x>>y;
        add(x, y, 0.0);//输入,把点x到y的“距离”(距离:再填线)设置为0,(因为点x到点y,本就可以到达,就不用再添加线) 
    }
    sort(e + 1, e + 1 + cnt, cmp);//快排,标准的kruskal 
     kruskal();
    printf("%.2lf\n", ans);//打印 
    return 0;//好习惯 
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值