最小生成树(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;
}
View Code

 


posted @ 2016-08-01 15:48  贱人方  阅读(662)  评论(0编辑  收藏  举报