D131 最小生成树 Kruskal 算法 P2872 [USACO07DEC] Building Roads S

D131 最小生成树 Kruskal 算法 P2872 [USACO07DEC] Building Roads S_哔哩哔哩_bilibili

 

P2872 [USACO07DEC] Building Roads S - 洛谷

给定 n 个点的坐标,给定 m 条边,要求添加一些边,使得所有点连通。求添加边的总长度的最小值。

思路

本题的边权就是两点之间的欧几里得距离,由题意可以看出是求最小生成树

关键是如何处理给定的 $m$ 条边?

只要把给定的 $m$ 条边的边权设为 $0$,那么跑 Kruscal 算法时,一定先选上这些边,剩下的 $n-1-m$ 条边的边权和就是答案

相关板子:

D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园

// 最小生成树 Kruskal算法 O(MlogM)
#include<bits/stdc++.h>
using namespace std;

const int N=1010,M=1000010;
int n,m,tot,cnt,x[N],y[N],fa[N];
double ans;
pair<double,pair<int,int> >e[M]; //边集

int find(int u){ //并查集的找根
  return fa[u]==u?u:fa[u]=find(fa[u]);
}
void kruskal(){
  for(int i=1;i<=n;i++) fa[i]=i;
  sort(e+1,e+cnt+1); //排序
  for(int i=1; i<=cnt; i++){
    int x=find(e[i].second.first),y=find(e[i].second.second);
    if(x!=y){
      fa[x]=y;
      ans+=e[i].first;
      if(++tot==n-1) break;
    }
  }
  printf("%.2lf\n",ans);
}
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++){
    scanf("%d %d",&x[i],&y[i]);
    for(int j=1;j<i;j++){
      double d=sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j]));
      e[++cnt]={d,{i,j}};
    }
  }
  for(int i=1,u,v; i<=m; i++){
    scanf("%d %d",&u,&v);
    e[++cnt]={0.0,{u,v}}; //m条边必选
  }
    
  kruskal();
}

 

posted @ 2026-04-14 21:29  董晓  阅读(70)  评论(0)    收藏  举报