最小生成树(Kruskal)(并查集)
最小生成树
时间限制: 1 Sec 内存限制: 64 MB
提交: 11 解决: 2
[提交][状态][讨论版]
题目描述
某个宇宙帝国有N个星球,由于宇宙的空间是三维的,因此每个星球的位置可以用三维坐标(X,Y,Z)来表示。任意两个不同的星球i和j都有一条边相连,边的距离是这样计算的:dis[i,j]=min(|Xi-Xj|,|Yi-Yj|,|Zi-Zj|),其中| | 符号表示取绝对值。现在让你来挑N-1条边,让这N个星球连通成一个最小生成树,输出构成最小生成树的N-1条边的长度总和。
输入
第1行,一个整数N(1≤N≤100000)。
接下来有N行,每行三个整数X,Y,Z,表示一个星球的坐标,-1000000000≤X,Y,Z≤1000000000。没有两个星球的位置完全重叠。
输出
1行,构成最小生成树的N-1条边的长度总和。
样例输入
5
11 -15 -15
14 -5 -15
-1 -1 -5
10 -4 -1
19 -4 19
样例输出
4
【分析】又一道最小生成树,还是Kruskal算法,本以为能很快写出来,但一开始的建边就把我难住了,注意N≤100000,如果用二重循环建边指定超时。
所以我们可以将x,y,z分别排序,然后用结构体放进优先队列,再Kruskal就行了。下面是AC代码。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <algorithm> #include <climits> #include <cstring> #include <string> #include <set> #include <map> #include <queue> #include <stack> #include <vector> #include <list> #include<functional> #define mod 1000000007 #define inf 0x3f3f3f3f #define pi acos(-1.0) using namespace std; typedef long long ll; const int N=100005; int n,m,k,cnt=-1; int d[4][2]= {0,1,1,0,0,-1,-1,0}; int parent[N+60]; bool vis[N]; struct man { int u,v,w; bool operator<(const man&a)const{ return w>a.w;//最小值优先 } } edg[N];//结构体存节点与节点之间的权值,x,y,z各存一份 priority_queue<man>q;//优先队列 struct mann { int x,y,z,id; }mp[N]; bool cmp1(mann g,mann h) {return g.x<h.x;} bool cmp2(mann g,mann h) {return g.y<h.y;} bool cmp3(mann g,mann h) {return g.z<h.z;}//分别对x,y,z从小到大排序 void init() { for(int i=0; i<=n; i++)parent[i]=i; }//初始化 int Find(int x) { if(parent[x] != x) parent[x] = Find(parent[x]); return parent[x]; }//查找并返回节点x所属集合的根节点 void Union(int x,int y) { x = Find(x); y = Find(y); if(x == y) return; parent[y] = x; }//将两个不同集合的元素进行合并 void Build()//将空间图转化为树,分别将两点间的x,y,z方向上的距离存到优先队列, { sort(mp,mp+n,cmp1); for(int i=1;i<n;i++){ man s; s.u=mp[i-1].id;s.v=mp[i].id;s.w=abs(mp[i-1].x-mp[i].x); q.push(s); }//从小到大排序,相邻两个数差距最小 sort(mp,mp+n,cmp2); for(int i=1;i<n;i++){ man s; s.u=mp[i-1].id;s.v=mp[i].id;s.w=abs(mp[i-1].y-mp[i].y); q.push(s); } sort(mp,mp+n,cmp3); for(int i=1;i<n;i++){ man s; s.u=mp[i-1].id;s.v=mp[i].id;s.w=abs(mp[i-1].z-mp[i].z); q.push(s); } } void Kruskal() { ll sumweight=0;//生成树的总权值 int num=0;//已经选用边的数目 int u,v;//顶点 init(); while(1) { man t=q.top();q.pop(); u=t.u; v=t.v; if(Find(u)!=Find(v)) { sumweight+=t.w; num++; Union(u,v); } if(num>=n-1) break;//边已经全部建好 } printf("%lld\n",sumweight); } int main() { memset(vis,false,sizeof(vis)); int u,v,w; cin>>n; for(int i=0; i<n; i++){ cin>>mp[i].x>>mp[i].y>>mp[i].z;mp[i].id=i; } Build(); Kruskal(); return 0; }