D130 最小生成树 Kruskal 算法 P1550 [USACO08OCT] Watering Hole G

D130 最小生成树 Kruskal 算法 P1550 [USACO08OCT] Watering Hole G_哔哩哔哩_bilibili

 

P1550 [USACO08OCT] Watering Hole G - 洛谷      U438765 新的开始 - 洛谷

给 n 个点的点权和两两之间的边权,怎样选点和选边使得总花费最小。

思路

图论算法大部分基于边权,有点权时通常把点权转化为边权

引入一个超级源点 $0$,把超级源点与 $n$ 个点连边,把 $n$ 个点的点权转移到边上

选择在 $i$ 点上打井,等价于选 $0$ 与 $i$ 之间的连边,问题变成 $n+1$ 点的最小生成树的边权和,跑 Kruscal 算法

相关板子:

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

 

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

const int N=310,M=90010;
int n,m,tot,ans,fa[N];
pair<int,pair<int,int> >e[M]; //边集

int find(int u){ //并查集的找根
  return fa[u]==u?u:fa[u]=find(fa[u]);
}
void kruskal(){
  sort(e+1,e+m+1); //排序
  for(int i=0; i<=n; i++) fa[i]=i;
  for(int i=1; i<=m; 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) break;
    }
  }
  cout<<ans;
}
int main(){
  cin>>n;
  for(int i=1,x;i<=n;i++){
    cin>>x;
    e[++m]={x,{0,i}}; //超级源点0
  }
  for(int i=1;i<=n;i++){
    for(int j=1,x;j<=n;j++){
      cin>>x;
      if(i!=j) e[++m]={x,{i,j}};
    }
  }

  kruskal();
}

 

posted @ 2026-04-13 15:25  董晓  阅读(83)  评论(0)    收藏  举报