D08【模板】最小生成树 Kruskal 算法

D08 最小生成树 Kruskal 算法_哔哩哔哩_bilibili

 

最小生成树 - OI Wiki

Kruskal 算法 是个贪心算法,基本思想是从小到大加入边

维护一堆 集合,查询两个元素是否属于同一集合,合并两个集合

查询两点是否连通和连接两点 可以使用并查集维护

排序 𝑂(𝑚log⁡𝑚) ,并查集 𝑂(𝑚𝛼(𝑚,𝑛)) ,时间复杂度为 𝑂(𝑚log⁡𝑚)

 

P3366 【模板】最小生成树 - 洛谷        P1195 口袋的天空 - 洛谷

求最小生成树的边权和。

// Kruskal算法 O(mlogm)
#include<bits/stdc++.h>
using namespace std;

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

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+1+m); //排序
  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-1) break;
    }
  }
  if(tot==n-1) printf("%d\n",ans);
  else puts("orz");
}
int main(){
  cin>>n>>m;
  for(int i=1,u,v,w;i<=m;i++){
    cin>>u>>v>>w;
    e[i]={w,{u,v}};
  }
  kruskal();
}

 

P1546 [USACO3.1] 最短网络 Agri-Net - 洛谷

求最小生成树的边权和。

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

const int N=110,M=10010;
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=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;
      ans+=e[i].first;
      if(++tot==n-1) break;
    }
  }
  cout<<ans;
}
int main(){
  cin>>n;
  for(int i=1; i<=n; i++)
  for(int j=1,k; j<=n; j++){
    cin>>k;
    e[++m]={k,{i,j}};
  }
  
  kruskal();
}

 

P2820 局域网 - 洛谷

去掉的最大边权和 $=$ 总边权和 $-$ 最小生成树的边权和 

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

const int N=110,M=210;
int n,m,tot,sum,ans,fa[N];
struct E{int x,y,w;}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+m+1,[&](E a,E b){return a.w<b.w;});
  for(int i=1; i<=m; i++){
    int x=find(e[i].x),y=find(e[i].y);
    if(x!=y){
      fa[x]=y;
      ans+=e[i].w;
      if(++tot==n-1) break;
    }
  }
  cout<<sum-ans;
}
signed main(){
  cin>>n>>m;
  for(int i=1,x,y,w;i<=m;i++){
    cin>>x>>y>>w;
    sum+=w;
    e[i]={x,y,w};
  }
  
  kruskal();
}

 

P1669 [USACO04DEC] Bad Cowtractors S - 洛谷

求最大生成树的边权和。

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

const int N=1010,M=20010;
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); //排序
  reverse(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-1) break;
    }
  }
  if(tot==n-1) printf("%d\n",ans);
  else puts("-1");
}
int main(){
  cin>>n>>m;
  for(int i=1,u,v,w;i<=m;i++){
    cin>>u>>v>>w;
    e[i]={w,{u,v}};
  }
  
  kruskal();
}

 

P2212 [USACO14MAR] Watering the Fields S - 洛谷

求最小生成树的边权和。

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

const int N=2010,M=4000010;
int n,c,m,tot,ans,x[N],y[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;
      ans+=e[i].first;
      if(++tot==n) break;
    }
  }
  if(tot==n-1) printf("%d\n",ans);
  else puts("-1");
}
int main(){
  cin>>n>>c;
    for(int i=1;i<=n;i++){
        scanf("%d %d",&x[i],&y[i]);
        for(int j=1;j<i;j++){
            int d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
            if(d>=c) e[++m]={d,{i,j}};
        }
    }
    
  kruskal();
}

 

P2847 [USACO16DEC] Moocast G - 洛谷

求最小生成树的最大边权。

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

const int N=1010,M=1000010;
int n,c,m,tot,ans,x[N],y[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;
      ans=e[i].first;
      if(++tot==n-1) break;
    }
  }
  printf("%d\n",ans);
}
int main(){
  cin>>n;
  for(int i=1;i<=n;i++){
    scanf("%d %d",&x[i],&y[i]);
    for(int j=1;j<i;j++){
      int d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
      e[++m]={d,{i,j}};
    }
  }
    
  kruskal();
}

 

P2330 [SCOI2005] 繁忙的都市 - 洛谷          P1547 [USACO05MAR] Out of Hay S - 洛谷

求最小生成树的边数和最大边权。

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

const int N=2010,M=40010;
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=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;
      ans=e[i].first;
      if(++tot==n-1) break;
    }
  }
  cout<<n-1<<' '<<ans;
}
int main(){
  cin>>n>>m;
  for(int i=1,u,v,c; i<=m; i++){
    cin >>u>>v>>c;
    e[i]={c,{u,v}};
  }
  
  kruskal();
}

 

posted @ 2022-05-28 13:19  董晓  阅读(2003)  评论(0)    收藏  举报