D134 最小生成树 Kruskal 算法 P3073 [USACO13FEB] Tractor S

D134 最小生成树 Kruskal 算法 P3073 [USACO13FEB] Tractor S_哔哩哔哩_bilibili

 

P3073 [USACO13FEB] Tractor S - 洛谷

方格图,尽量走边权最小的格点,连通块的格点数达到一半(奇数时一半+1),输出连通块内的最大边权。

思路

对格点按坐标连续编号,对相邻的格点,用高度差的绝对值做边权连边

显然是求最小生成树的最大边权,跑 Kruskal 算法,记录一下并查集的大小,当 $siz[y]\ge \frac{n+1}{2}$ 时,输出边权

左图连通块内的格点数 $>5$ 时,已经加完所有点,边权为 5;右图连通块内的格点数 $= 5$ 时,边权为 1

相关板子:

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

 

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

const int N=250010,M=N*4;
int n,m,tot,ans,mi,a[505][505],fa[N],siz[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,siz[i]=1;
  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;
      siz[y]+=siz[x];
      ans=e[i].first;
      if(siz[y]>=(n+1)/2) break;
    }
  }
  cout<<ans;
}
int P(int i,int j){ //给格点编号
  return (i-1)*n+j;
}
signed main(){
  cin>>n;
  for(int i=1;i<=n;i++)for(int j=1;j<=n;++j)cin>>a[i][j];
  for(int i=1;i<=n;++i)for(int j=1;j<=n;++j){
    if(i>1) e[++m]={abs(a[i][j]-a[i-1][j]),{P(i,j),P(i-1,j)}};
    if(j>1) e[++m]={abs(a[i][j]-a[i][j-1]),{P(i,j),P(i,j-1)}};
    if(i<n) e[++m]={abs(a[i][j]-a[i+1][j]),{P(i,j),P(i+1,j)}};
    if(j<n) e[++m]={abs(a[i][j]-a[i][j+1]),{P(i,j),P(i,j+1)}};
  }
  n*=n;
  kruskal();
}

 

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