D133 最小生成树 Kruskal 算法 P2916 [USACO08NOV] Cheering up the Cow G

D133 最小生成树 Kruskal 算法 P2916 [USACO08NOV] Cheering up the Cow G_哔哩哔哩_bilibili

 

P2916 [USACO08NOV] Cheering up the Cow G - 洛谷

保持各牧场连通的情况下去掉尽量多的道路。选一个点出发,最后回到这个点。访问点和边都花费时间,求最小时间。

思路

第一句话表明剩下了一颗树,第二句话表明每条边访问两次,每条边的端点各访问一次

所以一条边的贡献为 $2*w_{u,v}+a_u+a_v$,就把这个值作为这条边的新边权

然后跑 Kruskal 算法,求最小生成树的边权和 $sum$,注意 $sum$ 并不是最终答案

还要加上最终回到起点的点权,需要在 $n$ 个点中选出最小的点权 $mi$ 加上

image

相关板子:

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

 

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

const int N=10010,M=100010;
int n,m,tot,sum,mi,a[N],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=1; 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;
      sum+=e[i].first;
      if(++tot==n-1) break;
    }
  }
  cout<<sum+mi;
}
signed main(){
  cin>>n>>m; mi=1000;
  for(int i=1;i<=n;i++)cin>>a[i],mi=min(mi,a[i]);
  for(int i=1,u,v,w;i<=m;i++){
    cin>>u>>v>>w;
    e[i]={2*w+a[u]+a[v],{u,v}}; //点权并入边权
  }
  
  kruskal();
}

 

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