1. 程式人生 > >UVa Sculpture(離散化 floodfill)

UVa Sculpture(離散化 floodfill)

ace 每一個 += tin tro area blog 開始 紅點

題意:

給定n個立方體的一個頂點坐標和3邊長度, 問這些立方體組成的雕塑的表面積和體積, 坐標都是整數,n最大為50, 最大為500, 邊長最大也是500。

分析:

繼UVa221後又一道離散化

首先先深入理解一下離散化: (轉自 http://www.cnblogs.com/jerryRey/p/4599388.html)

先來看一個問題:給你以下的網格,你需要多少空間去存儲紅點區間的信息呢?

技術分享

只需要圖上所示的1,2,3,4個點就足夠表示紅點所在區間了,為什麽不是一個區間的第一個紅點和最後一個紅點呢?(如果這樣記錄的話則必須加一區間點,記錄區間內部信息,因為端點可能是兩個區間的交集而區間內可能只被操作了一次)這樣做的好處是空白區域的長度也能輕易計算出來。

因此離散化的核心在於以點代表區間

技術分享

對於本題, 如果直接建一個1000*1000*1000的數組進行floodfill , 方法是可行的, 但是從數據上看可能會爆內存+超時, 因為這裏數據規模已經到了1e9.

所以我們需要離散化出我們要用的坐標, 對於50個立方體來說, 我們每一個維度(xyz)最大會有50*2 = 100個不同的坐標, 那麽我們就可以把這些坐標的區間段記錄下來, 這些區間段的性質都是一樣的(要麽沒有東西, 要麽是立方體), 所以我們就可以把一個區間離散化為一個點, 然後將這些點填入一個100*100*100的數組做floodfill (數據規模1e6)。

因為本題還有有很多細節的地方, 所以我沒有獨立編程去實現, 貼一個對劉汝佳代碼註釋過的代碼。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn = 50 +5 ;
  7 const int maxc = 1000 + 1;
  8 
  9 //原來的數據
 10 int n, x0[maxn], x1[maxn], y0[maxn], y1[maxn], z0[maxn], z1[maxn];
 11 
 12 //離散化 (將每個線段離散為一個點)
13 int nx, ny, nz; 14 int xs[maxn*2], ys[maxn*2], zs[maxn*2]; 15 16 //六個方向 如果是xz面看的話 就是 右左前後上下 17 const int dx[] = {1,-1,0,0,0,0}; 18 const int dy[] = {0,0,1,-1,0,0}; 19 const int dz[] = {0,0,0,0,1,-1}; 20 21 //離散化數組,每一個點都是代表原來的一個線段(不一定是原來的矩形, 可能是矩形的一部分), 在這個坐標內floodfill 22 int color[maxn*2][maxn*2][maxn*2]; 23 24 // 25 struct cube{ 26 int x, y, z; //只要有xyz這個點 , 就能在color數組內對應空氣或者實體 27 cube(int x = 0, int y = 0, int z = 0): x(x) , y(y) , z(z){} 28 bool valid() const{ 29 //每個點的左閉右開區間代表一個線段, 而且最後一個點就是範圍極限(它不代表一個線段), 所以範圍應該是[0, 最後一個點的前一個] 30 //下標從0開始 一直到 nx - 1 個點 31 return x >= 0 && x < nx - 1 && y >= 0 && y < ny-1 && z>=0 && z < nz -1; 32 } 33 34 bool solid() const{ 35 //判斷這個點代表的是空氣還是實體 36 return color[x][y][z] == 1; 37 } 38 39 bool getvis() const{ 40 //判斷是否被訪問過 41 return color[x][y][z] == 2; 42 } 43 44 void setvis() const{ 45 color[x][y][z] = 2; 46 } 47 //訪問他的鄰居 訪問要先判斷是否 valid 48 cube neighbor(int dir) const{ 49 return cube(x+dx[dir], y+dy[dir], z+dz[dir]); 50 } 51 int volume() const{ 52 //要計算這個color點的體積, 要重新找回這個color點原始的數據 53 return (xs[x+1] - xs[x]) * (ys[y+1] -ys[y]) * (zs[z+1]-zs[z]); 54 } 55 int area(int dir) const{//計算表面積 56 //想象一下 如果遍歷過程中向上碰到solid, 那麽說明這肯定是一個 xy型的面, 所以可以加上x*y 57 if(dx[dir] != 0) return (ys[y+1] - ys[y]) * (zs[z+1]-zs[z]); 58 else if(dy[dir] != 0) return (xs[x+1] - xs[x]) * (zs[z+1] - zs[z]); 59 return (xs[x+1] - xs[x]) * (ys[y+1] - ys[y]); 60 } 61 }; 62 63 void discretize(int* x, int& n){ 64 sort(x,x+n); //默認升序 65 n = unique(x,x+n) - x; // 這樣就可以求出有多少個不同的元素 66 } 67 int ID(int* x, int n, int x0){//使原來矩形的數據對應離散化數組的下標 68 return lower_bound(x, x+n, x0) - x; //返回一個等於x0的下標 69 } 70 71 void floodfill(int&v , int& s){ 72 v = 0; 73 s = 0; 74 cube c; 75 c.setvis(); 76 queue<cube> q;//碰到空氣就入隊, 碰到solid計算表面積 77 q.push(c); 78 while(!q.empty()){ 79 cube c = q.front(); q.pop(); 80 v += c.volume(); //出隊空氣的體積 81 for(int i = 0; i < 6; i++){ 82 cube c2 = c.neighbor(i); 83 if(!c2.valid()) continue; 84 if(c2.solid()) s += c.area(i); 85 else if(!c2.getvis()){ 86 c2.setvis(); 87 q.push(c2); 88 } 89 } 90 } 91 v = maxc*maxc*maxc - v; 92 } 93 94 int main(){ 95 int T; 96 scanf("%d", &T); 97 while(T--){ 98 nx = ny = nz = 2; 99 xs[0] = ys[0] = zs[0] = 0;//給定下界 讓空氣可以嚴格包圍矩形 100 xs[1] = ys[1] = zs[1] = maxc; // 同上 101 102 scanf("%d", &n); 103 for(int i = 0; i < n ;i++){ 104 scanf("%d %d %d %d %d %d", &x0[i], &y0[i], &z0[i], &x1[i], &y1[i], &z1[i]); 105 x1[i] += x0[i], y1[i] += y0[i], z1[i] += z0[i]; 106 107 xs[nx++] = x0[i], xs[nx++] = x1[i]; 108 ys[ny++] = y0[i], ys[ny++] = y1[i]; 109 zs[nz++] = z0[i], zs[nz++] = z1[i]; 110 } 111 discretize(xs,nx);//離散化, 傳入一個數組坐標和一個應用變量, 去重輸出有多少段 112 discretize(ys,ny); 113 discretize(zs,nz); 114 memset(color, 0, sizeof(color));//初始化color 準備用坐標填入 115 for(int i = 0; i < n; i++){ 116 int X1 = ID(xs,nx,x0[i]), X2 = ID(xs,nx,x1[i]); 117 int Y1 = ID(ys,ny,y0[i]), Y2 = ID(ys,ny,y1[i]); 118 int Z1 = ID(zs,nz,z0[i]), Z2 = ID(zs,nz,z1[i]); 119 //左開右閉區間代表線段 120 for(int X = X1; X < X2; X++){ 121 for(int Y = Y1; Y < Y2; Y++){ 122 for(int Z = Z1; Z < Z2; Z++){ 123 color[X][Y][Z] = 1; 124 } 125 } 126 } 127 } 128 int v, s; 129 floodfill(v, s); 130 printf("%d %d\n",s,v); 131 } 132 }

UVa Sculpture(離散化 floodfill)