UVa Sculpture(離散化 floodfill)
阿新 • • 發佈:2017-07-29
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)