uva12171 sculpture 离散化 3维bfs求面积体积

  1. 离散化。
  2. 状态数组是关键。
  3. 求面积利用投影和6个方向就可以解决。

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=24845
看了很久才看懂,代码是网上找的,注释是自己写的,终于理解透彻了。
离散化>>bfs
求面积:一个实点利用投影和6个方向可以求得。求垂直于遍历方向的面。
求体积:利用一个实点直接求得。//此题不行,需要先计算空气的体积然后相减,空气的体积利用遍历空气求得。
状态数组的使用很是关键。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<string>

using namespace std;

int u[6]={0,0,0,0,1,-1};//x的状态
int v[6]={1,-1,0,0,0,0};//y的状态
int w[6]={0,0,1,-1,0,0};//z的状态

struct rec{//用于输入时的值
    int x1,y1,z1,x2,y2,z2;
};

struct POINT{//点
    int x,y,z;
};

bool vis[103][103][103];//访问过的点
bool loc[103][103][103];//保存墙的点,只保存一个角的点,利用bfs梯度计算
int n,T,ans1,ans2;//面积和体积
int x[103],y[103],z[103];//用于离散化
rec p[103];//保存长方体的值
int numx,numy,numz;//保存最大值,用于最后的输出

int IDX(int aim){//用于返回长方体的位置
    return lower_bound(x,x+numx,aim)-x;
}

int IDY(int aim){
    return lower_bound(y,y+numy,aim)-y;
}

int IDZ(int aim){
    return lower_bound(z,z+numz,aim)-z;
}

/*
*因为减一如果探测到的话,那么表示的是投影,面积一样,比如x方向插入那么计算的是垂直的面的面积
*/
int solve(int flag,int nowx,int nowy,int nowz){//计算表面积
    if (flag==0) return (x[nowx+1]-x[nowx])*(z[nowz+1]-z[nowz]);
    if (flag==1) return (x[nowx+1]-x[nowx])*(z[nowz+1]-z[nowz]);
    if (flag==2) return (x[nowx+1]-x[nowx])*(y[nowy+1]-y[nowy]);
    if (flag==3) return (x[nowx+1]-x[nowx])*(y[nowy+1]-y[nowy]);
    if (flag==4) return (z[nowz+1]-z[nowz])*(y[nowy+1]-y[nowy]);
    if (flag==5) return (z[nowz+1]-z[nowz])*(y[nowy+1]-y[nowy]);
    return -1;
}

void bfs(){
    queue<POINT> q;//bfs()
    q.push((POINT){0,0,0});
    ans2=(x[1]-x[0])*(y[1]-y[0])*(z[1]-z[0]);
    vis[0][0][0]=1;
    while (!q.empty())
    {
        POINT now=q.front();
        q.pop();
        for (int i=0;i<6;i++)
        {//6个方向进行bfs
            int tx=now.x+u[i];
            int ty=now.y+v[i];
            int tz=now.z+w[i];
            if (tx<0 || tx>=numx-1 || ty<0 || ty>=numy-1 || tz<0 || tz>=numz-1 || vis[tx][ty][tz])
                continue;//保证不出界且没有访问过
            if (loc[tx][ty][tz])
            {//遇到实点则计算面积,由于有6个方向遍历,所以可以访问完6个面。
                ans1+=solve(i,now.x,now.y,now.z);
            }
            else
            {
                ans2+=(x[tx+1]-x[tx])*(y[ty+1]-y[ty])*(z[tz+1]-z[tz]);//计算空气的面积
                vis[tx][ty][tz]=1;
                q.push((POINT){tx,ty,tz});//以空气节点为核心遍历
            }
        }
    }
}

int main(){
    scanf("%d",&T);
    for (int cas=1;cas<=T;cas++){
        scanf("%d",&n);
        for (int i=0;i<n;i++){
            scanf("%d%d%d%d%d%d",&p[i].x1,&p[i].y1,&p[i].z1,&p[i].x2,&p[i].y2,&p[i].z2);
            p[i].x2+=p[i].x1;
            p[i].y2+=p[i].y1;
            p[i].z2+=p[i].z1;
            x[2*i+1]=p[i].x1;//0位不保存值,用于第一个空气遍历
            x[2*i+2]=p[i].x2;
            y[2*i+1]=p[i].y1;
            y[2*i+2]=p[i].y2;
            z[2*i+1]=p[i].z1;
            z[2*i+2]=p[i].z2;
        }
        x[0]=0;
        y[0]=0;
        z[0]=0;
        x[2*n+1]=1001;
        y[2*n+1]=1001;//表明最大值,边界
        z[2*n+1]=1001;//拓展范围
        sort(x,x+2*n+2);
        sort(y,y+2*n+2);
        sort(z,z+2*n+2);
        numx=unique(x,x+2*n+2)-x;
        numy=unique(y,y+2*n+2)-y;
        numz=unique(z,z+2*n+2)-z;//离散化
        memset(loc,0,sizeof(loc));
        memset(vis,0,sizeof(vis));
        for (int now=0;now<n;now++){//将内部长方形包裹住,所以面积不重复计算,但是相当于把壳分成很多块儿计算。小的体积算在大的之内
            for (int i=IDX(p[now].x1);i<IDX(p[now].x2);i++){
                for (int j=IDY(p[now].y1);j<IDY(p[now].y2);j++){
                    for (int k=IDZ(p[now].z1);k<IDZ(p[now].z2);k++){
                        loc[i][j][k]=1;//记录矩形位置,只保存一个角
                    }
                }
            }
        }
        ans1=0;//^2
        ans2=0;//^3
        bfs();
        ans2=x[numx-1]*y[numy-1]*z[numz-1]-ans2;//总体积减去空气体积
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值