1. 程式人生 > >12171 Sculpture 離散化,網格與點的轉化

12171 Sculpture 離散化,網格與點的轉化

某雕塑由 n (n<=50) 個邊平行於座標軸、座標在1到1000內的的長方體組成。統計這個雕像的體積和表面積。注意,雕像內部可能會有密閉的空間,其體積應該算在總體積中,而其面積不算在表面積中。

分析

注意這道題的基本單位是體積為1立方單位的小方塊,所以可以把每個長方體都看成對若干小方塊的填充。未填充的塊視為空氣,則:
體積 = 總空間體積 - 外部連通的空氣體積
外表面積 = 外部連通的空氣與雕塑的接觸面面積之和

注意題目中給出的都是點的座標而不是方塊的座標,為了方便地判斷連通性與體積,應當將長方體的邊界點表示轉換成小方塊的座標表示。
編號(i,j,k)表示x在(i,i+1),y在(j,j+1),z在(k,k+1)內的一個小方塊。每個小方塊的體積為1,每個面的面積為1。


舉例來說,長方體(1,2,3,1,2,3)表示x在(1,2),y在(2,4),z在(3,6)內,它填充的小方塊包括:(1,2,3),(1,3,3),(1,2,4),(1,3,4),(1,2,5),(1,3,5).

如果這樣做的話,小方塊最多有1000*1000*1000=1e9個,顯然無法處理的,而每個維度上最多隻有50*2=100個座標,所以可以離散化,只需要處理最多100*100*100=1e6個小方塊。
對每個維度的所有點座標分別進行離散化,將對映和逆對映分別儲存在map<int,int> dct和陣列int idct[3][100]中。

現在,編號(i,j,k)的“小方塊”表示的區域為
x(idct0[i],i

dct0[i+1]),y(idct1[j],idct1[j+1]),z(idct2[k],idct2[k+1])x在(idct_0[i],idct_0[i+1]),y在(idct_1[j],idct_1[j+1]),z在(idct_2[k],idct_2[k+1])內。
d0=(idct0[i+1]idct0[i]),d1=(idct1[j+1]idct1[j]),d2=(idct2[k+1]idct2[k])d_0 = (idct_0[i+1]-idct_0[i]),d_1=(idct_1[j+1]-idct_1[j]),d_2=(idct_2[k+1]-idct_2[k])

則體積為d0d1d2d_0*d_1*d_2,x方向的面積為d1d2d_1*d_2,y方向的面積為d0d2d_0*d_2,z方向的面積為d0d1d_0*d_1

資料結構

map<int,int> dct[3]; //從原始座標到離散化座標,3個維度,dct = DisCreTization(離散化)
int did[3][100]; //離散化後每個區域的長度,3個維度,didct = Difference Inverse Dct(逆離散化的差分陣列)
int fil[100][100][100]; //表“小方塊”是否被填充及是否被訪問,0表示未被訪問的空氣,1表示被填充,-1表示已被訪問的空氣
int cub[50][6]; //原始資料,長方體的六維,cub = cuboid
int lim[3]; //每個維度的資料個數,也即方塊個數,lim = limit

演算法

  1. 初始化資料結構,讀入資料。
  2. 建立離散化對映,將資料離散化,填充小方塊。
  3. floodfill過程中計算體積與表面積。
  4. 輸出。

debug

最大邊界是1000,新增空氣只添加了1000,改成1024後過題。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
//using namespace std; 
using ll = long long; inline int read();
const int M = 128, MOD = 1000000007;

std::map<int,int> dct[3];
int did[3][M];
int lim[3];
int cub[M][6];
char fil[M][M][M];
void init()
{
	//init
	for(int i=0;i<3;++i) dct[i].clear();
	memset(did,0,sizeof(did));
	memset(lim,0,sizeof(lim));
	memset(cub,0,sizeof(cub));
	memset(fil,0,sizeof(fil));

	//read
	int n = read();
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<3;++j) cub[i][j] = read();
		for(int j=3;j<6;++j) cub[i][j] = cub[i][j-3] + read();
	}

	//discretization
	for(int i=1;i<=n;++i)
		for(int d=0;d<3;++d) //維度
			dct[d][cub[i][d]], dct[d][cub[i][d+3]];
	for(int d=0,cnt;d<3;++d)
	{
		dct[d][0], dct[d][1024];

		cnt = 0;
		for(auto &x : dct[d])
			x.second = ++cnt;
		cnt = 0;
		for(auto x : dct[d])
		{
			did[d][cnt] += x.first;
			did[d][++cnt] = -x.first;
		}
		lim[d] = cnt - 1;
	}

	//fill
	for(int i=1;i<=n;++i)
	{
		int xl = dct[0][cub[i][0]], xr = dct[0][cub[i][3]];
		int yl = dct[1][cub[i][1]], yr = dct[1][cub[i][4]];
		int zl = dct[2][cub[i][2]], zr = dct[2][cub[i][5]];
		for(int x=xl;x<xr;++x)
			for(int y=yl;y<yr;++y)
				for(int z=zl;z<zr;++z)
					fil[x][y][z] = 1;
	}
}

const int go[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};
ll vol, are;
void dfs(int x,int y,int z)
{
	fil[x][y][z] = -1;
	vol -= did[0][x] * did[1][y] * did[2][z];
	for(int k=0;k<6;++k)
	{
		int nx = x+go[k][0], ny = y+go[k][1], nz = z + go[k][2];
		if(nx>=1 && nx<=lim[0] && ny>=1 && ny<=lim[1] && nz>=1 && nz<=lim[2])
		{
			if(fil[nx][ny][nz]==0)
				dfs(nx,ny,nz);
			else if(fil[nx][ny][nz]==1)
			{
				ll tare = 1;
				if(k/2 != 0) tare *= did[0][x];
				if(k/2 != 1) tare *= did[1][y];
				if(k/2 != 2) tare *= did[2][z];
				are += tare;
			}
		}
	}
}
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int T = read();
	while(T--)
	{
		init();
		vol = 1<<30, are = 0;
		dfs(1,1,1);
		printf("%lld %lld\n",are,vol );
	}

    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}