D138 最小生成树 Kruskal 算法 U440253 连接格点

D138 最小生成树 Kruskal 算法 U440253 连接格点_哔哩哔哩_bilibili

 

U440253 连接格点 - 洛谷            P5687 [CSP-S 2019 江西] 网格图 - 洛谷

给一个 N 行 M 列的点阵,相邻两点可以相连,已经给了一些连边,求连通所有格点还需要的最小花费。

思路

每个二维格点可以按坐标映射为一维节点,共 $n*m$ 个节点

先把已知边加入并查集,接下来就是求最小生成树

按 Kruskal 的贪心思想,先枚举每条竖边,如果可以加入树集,花费$+1$;再枚举每条横边,如果可以加入树集,花费$+2$

图中绿边为已知边(可以不是树),红边为后加边,答案为 3

相关板子:

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

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

const int N=1010;
int n,m,ans,fa[N*N];

int find(int u){ //并查集的找根
  return fa[u]==u?u:fa[u]=find(fa[u]);
}
signed main(){
  cin>>n>>m;
  for(int i=1; i<=n*m; i++) fa[i]=i;
  for(int x1,y1,x2,y2;~scanf("%d%d%d%d",&x1,&y1,&x2,&y2);){
    int x=(x1-1)*m+y1,y=(x2-1)*m+y2;
    fa[find(x)]=find(y); //已知边加入并查集
  }
  
  // 先连竖边
  for(int i=1; i<n; i++)for(int j=1; j<=m; j++){
    int x=(i-1)*m+j, y=i*m+j;
    x=find(x), y=find(y);
    if(x!=y) fa[x]=y,ans++;
  }
  // 再连横边
  for(int i=1; i<=n; i++)for(int j=1; j<m; j++){
    int x=(i-1)*m+j, y=(i-1)*m+j+1;
    x=find(x), y=find(y);
    if(x!=y) fa[x]=y, ans+=2;
  }
  printf("%d\n",ans);
}

 

P3141 [USACO16FEB] Fenced In P - 洛谷

Kruscal 贪心思想,删的边构成最小生成树

排序,枚举累计最短边的和

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=2.5e+5;
int A,B,n,m,a[N],b[N];

int main(){
  scanf("%d%d%d%d",&A,&B,&n,&m);
  for(int i=1; i<=n; i++) scanf("%d",a+i); a[++n]=A; //横坐标
  for(int i=1; i<=m; i++) scanf("%d",b+i); b[++m]=B; //竖坐标
  
  sort(a+1,a+n+1); sort(b+1,b+m+1);
  for(int i=n; i>=1; i--) a[i]-=a[i-1]; //横边长
  for(int i=m; i>=1; i--) b[i]-=b[i-1]; //竖边长
  sort(a+1,a+n+1); sort(b+1,b+m+1);
  
  ll ans=1ll*a[1]*(m-1)+1ll*b[1]*(n-1); //m-1条最短横边,n-1条最短竖边
  int i=2,j=2;
  while(i<=n && j<=m){
    if(a[i]<b[j]) ans+=1ll*a[i++]*(m-j+1); //删短横边
    else ans+=1ll*b[j++]*(n-i+1); //删短竖边
  }
  printf("%lld\n",ans);
}

 

posted @ 2026-04-19 08:26  董晓  阅读(93)  评论(0)    收藏  举报