본문 바로가기
Computer Science

블록 조립

by OKOK 2019. 3. 15.

 

※ 주의 사항


1. 응시자는 User Code 내 test() 함수를 구현해야 한다.

     정답을 출력하는 것이 아니라, 리턴하는 것임을 명심하라.


2. User Code에는 어떠한 헤더 파일도 추가할 수 없다.

     User Code 내 printf, cout 같은 표준입출력 함수 등을 절대로 사용해서는 안 된다.


3. Main은 수정할 수 없으며, 실제 채점시에도 그대로 사용된다. (단, srand(3)의 파라미터는 변경)




밑면의 [가로 x 세로] 넓이가 각각 [4 x 4]인 블록 부품이 30,000개 주어진다.
예를 들어, 아래 그림과 같이 <부품 1>의 각 기둥 높이가 1~3 사이의 값이고,


                        <부품 1>


<부품 2>의 각 기둥 높이가 5~7 사이일 때,


                        <부품 2>


두 부품의 기둥 면을 마주하여 조립하면, 완벽히 일치(기둥 사이에 틈새가 존재하지 않는)하는 육면체 완성품을 얻을 수 있다.
이렇게 조립한 육면체의 높이는 8이 된다.

Main에서 자동 생성되는 30,000개의 부품 데이터를 입력 받아, 완벽히 일치하는 육면체 완성품들을 생산하고자 한다.

 ※ 한개의 완성품은 항상 두개의 블럭으로 구성된다.

단, 완성품들의 높이의 총 합은 최대가 되어야 한다.

완성품들의 높이의 총 합의 최대값을 반환하는 test() 함수를 작성하라.


[채점 기준]
1. test() 함수의 실행시간이 적을수록 높은 점수를 부여한다.
2. 전체 실행 시간이 20초를 넘을 경우 실격처리 된다.


[입출력]

1. 입력과 출력은 Main에서 자동으로 처리하며, 응시자는 test() 함수에서 정답만 리턴 해야 한다.

2. 출력된 정답은 Visual Studio와 SCS(검정 서버)에서 서로 다르며, 구체적인 값은 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <stdlib.h>
 
#define MAX 30000
 
using namespace std;
 
extern int test(int module[][4][4]);
 
int main(void)
{
    static int module[MAX][4][4];
 
    srand(3); // 3 will be changed
 
    for (int tc = 1; tc <= 10; tc++)
    {
        for (int c = 0; c < MAX; c++)
        {
            int base = 1 + (rand() % 6);
            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    module[c][y][x] = base + (rand() % 3);
                }
            }
        }
        cout << "#" << tc << " " << test(module) << endl;
    }
 
    return 0;
}
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#define N 30000
#define M 120000
#define MAX(a,b) ((a>b)?(a):(b))
#define MIN(a,b) ((a<b)?(a):(b))
 
/*
1. 하나의 블록을 int로 저장한다
2. 만들어진 binary int가 똑같으면 만들수 있다고 본다.
3. A 그룹은 그대로의 모습
4. B 그룹은 거꾸로 뒤집어서 4방향으로 처리한 모습으로 하고
5. A와 B그룹이 완전히 일치할 때, 합쳐지도록 처리하도록 한다.
*/
struct _node{
    int pk;
    unsigned int binary;
    int cost;
}tmp[M];
_node A[N]; //A그룹의 정보를 저장
_node B[M]; //B그룹의 정보를 저장
bool visit[N]; //해당 블록의 사용 여부
int total;
 
/*
    A,B그룹을 나누어 생성
*/
void makeAB(int module[][4][4]){
    for (int i = 0; i < N; i++){
        int minv = 10, maxv = 0;
        for (int j = 0; j < 4; j++)
            for (int k = 0; k < 4; k++){
                minv = MIN(minv, module[i][j][k]);
                maxv = MAX(maxv, module[i][j][k]);
            } 
        //A모듈 생성
        visit[i] = false;
        A[i].pk = i;
        A[i].cost = minv;   
        for (int j = 0; j < 4; j++){
            B[i * 4 + j].pk = i, B[i * 4 + j].cost = maxv;
            for (int k = 0; k < 4; k++){
                //A모듈 생성
                A[i].binary <<= 2, A[i].binary |= (module[i][j][k] - minv);
                //좌우반전
                B[i * 4].binary <<= 2, B[i * 4].binary |= (maxv - module[i][j][3 - k]);
                //오른쪽한번 회전
                B[i * 4 + 1].binary <<= 2, B[i * 4 + 1].binary |= (maxv - module[i][3 - k][3 - j]);
                //한번더 회전
                B[i * 4 + 2].binary <<= 2, B[i * 4 + 2].binary |= (maxv - module[i][3 - j][k]);
                //한번더 회전
                B[i * 4 + 3].binary <<= 2, B[i * 4 + 3].binary |= (maxv - module[i][k][j]);
            }
        }
    }
}
 
inline void sort(_node *target, int start, int endint version){
    if (start < end){
        int mid = (start + end/ 2;
        sort(target, start, mid, version);
        sort(target, mid+1end, version);
        int i, j, k;
        for (i = start, j = mid + 1, k = start; i <= mid && j <= end; k++){
            if (version == 0){
                if (target[i].cost > target[j].cost) tmp[k] = target[i++];
                else tmp[k] = target[j++];
            }
            else{
                if (target[i].binary > target[j].binary) tmp[k] = target[i++];
                else tmp[k] = target[j++];
            }
        }
        for (; i <= mid; k++)tmp[k] = target[i++];
        for (; j <= end; k++)tmp[k] = target[j++];
        for (i = start; i <= end; i++)target[i] = tmp[i];
    }
}
   
// A[a_index] < B[b_index] return 1
// A[a_index] > B[b_index] return -1
int search(int a_index, int b_index){
    if (A[a_index].binary == B[b_index].binary){
        //일치하는 경우해당 binary값의 시작과 끝을 확인하고
        //시작과 끝까지  
        int startp = b_index, endp = b_index;
        while (B[startp - 1].binary == A[a_index].binary) 
            startp--
        while (B[endp + 1].binary == A[a_index].binary) 
            endp++
 
        int maxcost = 0, maxpk=-1
        for (int j = startp; j <= endp; j++)
            if (!visit[B[j].pk] && A[a_index].pk != B[j].pk && maxcost < B[j].cost)
                maxcost = B[j].cost, maxpk = B[j].pk;
        if (maxcost){  
            visit[maxpk] = visit[A[a_index].pk] = true;
            total += maxcost+A[a_index].cost; 
        }
        return 0;
    }
    else if (A[a_index].binary < B[b_index].binary)
        return 1;
    else
        return -1;
}  
 
int test(int module[][4][4]){
    //1. A, B 모듈 생성 
    total = 0;
    makeAB(module);
 
    //2. A, B 모듈 sort by desc
    sort(A, 0, N - 10);
    sort(B, 0, M-11);
 
    //3. A, B모듈을 맞추어 가장 큰 크기를 구하기
    for (int i = 0; i < N; i++){
        //이미 사용된 블록의 경우
        if (visit[A[i].pk]) continue;  
        int left = 0, right = M-1, res; 
        while (left <= right){
            int mid = (left + right) / 2;
            //A모듈과 일치하는 B모듈을 찾은경우 
            if ((res = search(i, mid)) == 0)
                break
            else if (res>0//A모듈보다 B모듈이 큰경우, mid를 늘려야한다 desc이므로
                left = mid + 1;
            else
                right = mid - 1;
        }
    }  
    return total;
}
 
cs

 


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#define BLOCK_SIZE 30000  
  
#ifndef UINT  
#define UINT unsigned int  
#endif  
  
#define Move(x, from, to) \  
    ((x >> from & 3<< to)  
  
#define Change(x, \  
        a30, a28, a26, a24, \  
        a22, a20, a18, a16, \  
        a14, a12, a10, a8, \  
        a6, a4, a2, a0) \  
(\  
Move(x, a30, 30| Move(x, a28, 28| Move(x, a26, 26| Move(x, a24, 24| 
    Move(x, a22, 22| Move(x, a20, 20| Move(x, a18, 18| Move(x, a16, 16| 
    Move(x, a14, 14| Move(x, a12, 12| Move(x, a10, 10| Move(x, a8, 8|  
    Move(x, a6, 6| Move(x, a4, 4| Move(x, a2, 2| Move(x, a0, 0
)  
  
class Block {  
public:  
    UINT shape;  
    int height;  
    int depth;  
    Block() {  
        shape = height = depth = 0;  
    }  
};  
Block set0[BLOCK_SIZE], set1[BLOCK_SIZE], set2[BLOCK_SIZE];  
int cnt0, cnt1, cnt2;  
  
void init() {  
    cnt0 = cnt1 = cnt2 = 0;  
}  
  
#define HASH_MODULAR 33333  
#define HASH_TABLE_SIZE 70000  
#define HASH_BUCKET_SIZE 10  
  
UINT HShape[HASH_TABLE_SIZE];  
int HashCnt[HASH_TABLE_SIZE];  
int HashTable[HASH_TABLE_SIZE][HASH_BUCKET_SIZE];  
  
void initHash() {  
    for (int i = 0; i < HASH_TABLE_SIZE; ++i) {  
        HShape[i] = HashCnt[i] = 0;  
    }  
}  
  
void addHash(UINT shape, int value) {  
    int key = ((shape >> 16) % HASH_MODULAR) + (shape % HASH_MODULAR);  
  
    while (HShape[key] != shape && HShape[key] != 0) {  
        key = (key + 1) % HASH_TABLE_SIZE;  
        // shape 에 해당하는 key 가 이미 사용중일 경우,  
        // 1씩 key 값을 증가시키며 할당되지 않은 key 를 탐색  
    }  
  
    HShape[key] = shape;  
    HashTable[key][HashCnt[key]++= value;  
}  
  
int removeHash(UINT shape) {  
    int key = ((shape >> 16) % HASH_MODULAR) + (shape % HASH_MODULAR);  
  
    while (HShape[key] != shape) {  
        if (HShape[key] == 0)  
            return -1;    // 일치하는 모양이 없음.  
        key = (key + 1) % HASH_TABLE_SIZE;  
        // shape 에 해당하는 key 를 찾기 위해 1씩 key 값을 증가시키며 탐색  
    }  
  
    if (HashCnt[key] == 0)  
        return -1;  // 일치하는 모양의 블록을 다 사용하였음.  
  
    int maxIdx = 0;  
    for (int i = 1; i < HashCnt[key]; i++) {  
        if (HashTable[key][maxIdx] < HashTable[key][i])  
            maxIdx = i;  
    }  
    int maxValue = HashTable[key][maxIdx];  
    HashTable[key][maxIdx] = HashTable[key][HashCnt[key] - 1];  
    HashCnt[key]--;  
    return maxValue;  
}  
  
void makeBlock(int info[4][4]) {  
    Block b;  
    int max = 0;  
    int min = 10;  
    for (int i = 0; i < 4; i++) {  
        for (int j = 0; j < 4; j++) {  
            if (max < info[i][j])  
                max = info[i][j];  
            if (min > info[i][j])  
                min = info[i][j];  
        }  
    }  
  
    b.height = min;  
    b.depth = max - min;  
  
    int k = 30;  
    for (int i = 0; i < 4; i++) {  
        for (int j = 0; j < 4; j++, k -= 2) {  
            b.shape |= ((info[i][j] - min) << k);  
        }  
    }  
  
    // 4 방향 돌리면서 더 큰 숫자로 셋팅함.  
  
    // 1. 초기 상태의 모습  
    //         30 28 26 24  
    //         22 20 18 16  
    //         14 12 10 08  
    //         06 04 02 00  
    UINT tmpShape, originShape = b.shape;  
  
    // 2. 90도 회전  
    tmpShape = Change(originShape,  
        6142230,  
        4122028,  
        2101826,  
        081624  
        );  
  
    if (tmpShape > b.shape)  
        b.shape = tmpShape;  
  
    // 3. 180도 회전  
    tmpShape = Change(originShape,  
        0246,  
        8101214,  
        16182022,  
        24262830  
        );  
  
    if (tmpShape > b.shape)  
        b.shape = tmpShape;  
  
    // 4. 270도 회전  
    tmpShape = Change(originShape,  
        241680,  
        2618102,  
        2820124,  
        3022146  
        );  
  
    if (tmpShape > b.shape)  
        b.shape = tmpShape;  
  
    if (b.depth == 2) {  
        set2[cnt2++= b;  
    } else if (b.depth == 1) {  
        set1[cnt1++= b;  
    } else {  
        set0[cnt0++= b;  
    }  
}  
  
int getMaxMachedBlock(const UINT srcShape) {  
    UINT target_shape;  
    UINT shape[4];  
  
    // 4 방향 타겟을 구한 뒤, 제일 큰 모양으로 탐색을 함.  
  
    // 1. 초기 상태의 모습  
    // 30 28 26 24    |    24 26 28 30  
    // 22 20 18 16    |    16 18 20 22  
    // 14 12 10 08    |    08 10 12 14  
    // 06 04 02 00    |    00 02 04 06  
  
    shape[0= Change(srcShape,  
        24262830,  
        16182022,  
        8101214,  
        0246  
        );  
  
    // 2. 90도 회전  
    // 06 14 22 30    |    30 22 14 06  
    // 04 12 20 28    |    28 20 12 04  
    // 02 10 18 26    |    26 18 10 02  
    // 00 08 16 24    |    24 16 08 00  
  
    shape[1= Change(srcShape,  
        3022146,  
        2820124,  
        2618102,  
        241680  
        );  
  
    // 3. 180도 회전  
    // 00 02 04 06    |    06 04 02 00  
    // 08 10 12 14    |    14 12 10 08  
    // 16 18 20 22    |    22 20 18 16  
    // 24 26 28 30    |    30 28 26 24  
  
    shape[2= Change(srcShape,  
        6420,  
        1412108,  
        22201816,  
        30282624  
        );  
  
    // 4. 270도 회전  
    // 24 16 08 00    |    00 08 16 24  
    // 26 18 10 02    |    02 10 18 26  
    // 28 20 12 04    |    04 12 20 28  
    // 30 22 14 06    |    06 14 22 30  
  
    shape[3= Change(srcShape,  
        081624,  
        2101826,  
        4122028,  
        6142230  
        );  
  
    target_shape = shape[0];  
    for (int i = 1; i <= 3; i++) {  
        if (shape[i] > target_shape)  
            target_shape = shape[i];  
    }  
    return removeHash(target_shape);  
}  
  
int test(int module[][4][4]) {  
    init();  
    for (int i = 0; i < BLOCK_SIZE; i++)  
        makeBlock(module[i]);  
    int sum = 0;  
    // 1. 평평한 모양이면 짝수개로 다 더함.  
    for (int i = 0; i < cnt0; i++) {  
        sum += (set0[i].height);  
    }  
    if (cnt0 % 2) {  
        int minVal = 6;  
        // 홀수개 일 경우, 가장 작은 것 1개 제외  
        for (int i = 0; i < cnt0; i++) {  
            if (minVal > set0[i].height) {  
                minVal = set0[i].height;  
            }  
        }  
        sum -= minVal;  
    }  
  
    // 2. 높이차가 1이면, 두 블럭의 shape를 더한것이 0x55555555 가 되어야함.  
    // 0x55555555 = 01010101010101010101010101010101 (2)  
    initHash();  
    for (int i = 0; i < cnt1; i++)  
        addHash(set1[i].shape, set1[i].height);  
  
    for (int i = 0; i < cnt1 - 1; i++) {  
        int srcHeight = removeHash(set1[i].shape);  
  
        if (srcHeight < 0)  
            continue;  
  
        UINT srcShape = 0x55555555 - set1[i].shape;  
        int matchedHeight = getMaxMachedBlock(srcShape);  
  
        if (matchedHeight > 0)  
            sum += (srcHeight + matchedHeight + 1);  
    }  
  
    // 3. 높이차가 2이면, 두 블럭의 shape를 더한것이 0xaaaaaaaa 가 되어야함.  
    // 0xaaaaaaaa = 10101010101010101010101010101010 (2)  
    initHash();  
    for (int i = 0; i < cnt2; i++)  
        addHash(set2[i].shape, set2[i].height);  
  
    for (int i = 0; i < cnt2 - 1; i++) {  
        int srcHeight = removeHash(set2[i].shape);  
  
        if (srcHeight < 0)  
            continue;  
  
        UINT srcShape = ((UINT) 0xaaaaaaaa- set2[i].shape;  
        int matchedHeight = getMaxMachedBlock(srcShape);  
  
        if (matchedHeight > 0)  
            sum += (srcHeight + matchedHeight + 2);  
    }  
    return sum;  
}  
 
cs

 


'Computer Science' 카테고리의 다른 글

중고차  (0) 2019.03.15
Table Calculator  (0) 2019.03.15
Photo  (0) 2019.03.15
이미지 복원하기2  (0) 2019.03.15
루빅스 큐브  (0) 2019.03.15

댓글