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); }
浙公网安备 33010602011771号