UPC-星区划分(并查集+枚举)

天空黑暗到一定程度,星辰就会熠熠生辉

星区划分

时间限制: 1 Sec 内存限制: 128 MB
提交: 1056 解决: 237
[状态] [提交] [命题人:外部导入]
题目描述
爱好天文的小七发明了一种太空分区方法,在这个方法中,宇宙里亮度相近的星星被划为同一个星区。空间中任意相邻或坐标相同的星星若亮度差不大于给定整数M,则这两个星星属于同一星区(两个星星可能会有相同的坐标)。

现给你一个空间n个星星的三维坐标(x,y,z),和亮度值v,每个星星的上、下、左、右、前、后的六个相邻位置被认为是与其相邻的。请你计算一下该空间内的星区数量。

输入
第一行1个正整数n。n<=1000
第二行 1个正整数m。m<=109
第二行到第n+1行,每行四个正整数x,y,z,v。0<=x,y,z,v<=109
输出
一行正整数k,代表星区数量。

样例输入 Copy
5
2
1 1 1 2
1 2 3 2
1 2 2 5
1 1 0 3
1 0 1 3
样例输出 Copy
3
思路:

暴力枚举每个点,判断周围六个点以及本身是否能够与他构成一个星区,如果可以构成同一星区但不在同一星区,要合并。
( 比赛的时候思路到这里就卡了,身为一名数据结构选手竟然没想到用并查集来维护。)

暴力枚举每一颗星星,然后搜索它的六个方向坐标和与自己相同坐标的亮度,亮度合
法的话就用并查集把他们的祖先统一,最后统计一下祖先是自己的数量就是星区的数量。
复杂度:1000 * 1000 * 7

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
struct node{
    int x,y,z,v;
    int root;
}a[maxn];
int n,m;
bool check(int i,int j){
    int x1=a[i].x,x2=a[j].x;
    int y1=a[i].y,y2=a[j].y;
    int z1=a[i].z,z2=a[j].z;
    int w1=a[i].v,w2=a[j].v;
    if(x1==x2+1&&y1==y2&&z1==z2||x1==x2-1&&y1==y2&&z1==z2||x1==x2&&y1==y2+1&&z1==z2||x1==x2&&y1==y2-1&&z1==z2||x1==x2&&y1==y2&&z1==z2+1||x1==x2&&y1==y2&&z1==z2-1||x1==x2&&y1==y2&&z1==z2)
        if(abs(w1-w2)<=m) return true;
    return false;
}
int Find(int x){
    if(x!=a[x].root) a[x].root=Find(a[x].root);
    return a[x].root;
}
void Union(int x,int y){
    x=Find(x),y=Find(y);
    if(x!=y){
        a[x].root=y;
    }
}
int main(){
   /// int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&a[i].x,&a[i].y, &a[i].z ,&a[i].v );
        a[i].root=i;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j) continue;
            else
                if(check(i,j)) Union(i,j);
    int res=0;
    for(int i=1;i<=n;i++)
        //cout<<a[i].root<<" ";
        if(a[i].root==i) res++;
    printf("%d\n",res);
    return 0;
}

最后放一个类似的题叭,应该是未改过的题~
题目描述
天文学家Doctor博士发明了一种太空分区方法,在这个方法中,宇宙里亮度相近的区域被划为同一个星区。空间中相邻两区域若亮度差不大于给定整数M,则这两区域属于同一星区。现给你一个空间的三维坐标图,每个坐标整点表示一个区域,其值表示其亮度,而其上、下、左、右、前、后六个区域被认为是与其相邻的。请你计算一下该空间内的星区数量。

输入
第一行三个正整数x、y、z(x、y、z<=50),表示空间的长宽高。
第二行一个整数M。
接下来为一行,xyz个0~255的整数,按照空间坐标大小的顺序由小到大依次给出每个区域的亮度。
说明:对于空间两点p1(x1,y1,z1)和p2(x2,y2,z2),p1<p2当且仅当(x1<x2)或者(x1=x2且y1<y2)或者(x1=x2且y1=y2且z1<z2)。

输出
一个整数,空间中的星区数量。
这个直接dfs就可以,因为坐标都是连续的,而且数据范围也比较小。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int num[55][55][55];
bool mp[55][55][55];
int d;
int n,m,h;
int dx[]={0,0,-1,1,0,0};
int dy[]={1,-1,0,0,0,0};
int dz[]={0,0,0,0,1,-1};
bool check(int x,int y,int z)
{
    if(x<1||x>n||y<1||y>m||z<1||z>h)
        return 1;
    if(mp[x][y][z])
        return 1;
    return 0;
}
void dfs(int x,int y,int z,int p){
    mp[x][y][z]=true;
    for(int i=0; i<6; ++i){
        int next_x=x+dx[i];
        int next_y=y+dy[i];
        int next_z=z+dz[i];
        if(next_x>=1&&next_x<=n&&next_y>=1&&next_y<=m&&next_z>=1&&next_z<=h&&!mp[next_x][next_y][next_z]&&abs(num[next_x][next_y][next_z]-p)<=d)
        {
            dfs(next_x,next_y,next_z,num[next_x][next_y][next_z]);
        }
    }
}
int main(){
   scanf("%d%d%d%d",&n,&m,&h,&d);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            for(int k=1;k<=h;++k){
                scanf("%d",&num[i][j][k]);
            }
        }
    }
    int ent=0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            for(int k=1;k<=h;++k){
                if(!mp[i][j][k]){
                    ent++;
                    dfs(i,j,k,num[i][j][k]);
                }
            }
        }
    }
    printf("%d\n",ent);
    return 0;
}
//出自:Dili_iiii 

参考博客:【dfs模板】dfs找联通块分区 - Dili_iiii - 博客园

其实这场比赛打的挺自闭的,好几个人讨论这个题也没想到并查集,最后想到的时候不想写了。但是真的嘴上说着不想打了,却还是满腔热血的去vj拉题,这也许就是acm的魅力所在吧。

posted @ 2020-03-09 16:24  OvO1  阅读(89)  评论(0编辑  收藏  举报