기술 블로그

18808번 스티커 붙이기 본문

알고리즘 문제/BOJ

18808번 스티커 붙이기

parkit 2020. 3. 23. 22:36
728x90
반응형

https://www.acmicpc.net/problem/18808


삼성 sw 역량테스트, 코테, 코딩, 복습, 필수, 추천, BaaaaaaaaaaarkingDog, 코팅테스트, 모의고사, 브루트포스, 시뮬레이션, 구현, boj, 백준


바킹독님의 코딩 테스트 대비 모의고사 1번 문제이다.


바킹독님의 정답 코드 -  https://blog.encrypted.gg/938?category=730176



주석에 설명을 적었다.




0. 제일 첫 스티커는 무조건 원래 모양 그대로 (0, 0)칸이다.


1. 처음에 문제 이해를 못하고, 순서를 고려하지 않은 채 백트래킹 형식으로 구현했었다. 


2. 1번을 고친 후 첫 제출에서 틀렸다. 

알고보니 회전을 한 후 모든 좌표(0,0 ~ R-1,C-1)를 탐색하고 불가능하면 다른 회전을 해야하는데,

(0, 0)에서 출발해서 해당 좌표에 대해서 회전 4번을 모두 탐색하는 형식으로 구현하였고, 붙일 수 있다면 넘어갔었다.

하지만 문제의 의도는 모든 좌표에 대해서 탐색한 후에 적절한 붙일 수 있는 위치가 없다면, 그때, 90도 회전을 해야했다.


3. rt for문을 맨 바깥으로 빼고, 제출하니 정답.


아래는 틀린 코드(여기에서 rt for문을 맨 바깥으로 빼면 정답)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (int i = 0; i < R && go; i++) {
    for (int j = 0; j < C && go; j++) {
        for (int rt = 0; rt < 4 && go; rt++) {
            if (chk(n, i, j)) {
                draw(n, i, j, n + 1);
                go = false;
 
                backtracking(n + 1);
 
                draw(n, i, j, 0);
            }
 
            rotate(n);
        }
    }
}
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
#include<bits/stdc++.h>
 
using namespace std;
 
int R, C, K, M[102][12][12], temp[42][42];
pair<intint> p[102];
int Map[42][42];
/*
R, C, K 입력으로 주어지는 변수들(행, 열, 스티커 개수)
M[a][b][c] = a번 째 스티커에 대한 정보. b = 행, c = 열
temp[a][b] = 스티커를 회전(rotate)할 때 활용하는 임시 행렬
p[a] = a번 째 스티커에 대한 정보(행과 열에 대한 정보 즉, 길이)
Map[a][b] = 노트북(붙여진 스티커들을 알 수 있음)
*/
 
// 스티커를 붙일 수 있는지 검사
bool chk(int n, int a, int b)
{
    // 범위를 벗어나면 당연히 false
    if (a + p[n].first > R || b + p[n].second > C) {
        return false;
    }
 
    // r = i - a, c = j - b 라고 해도 된다.
    for (int i = a, r = 0; i < a + p[n].first; i++, r++) {    
        for (int j = b, c = 0; j < b + p[n].second; j++, c++) {
 
            // Map[i][j]가 1 이상인 것은 이미 스티커가 붙여져 있다는 뜻(스티커는 1 ~ K)
            // 스티커가 붙여져 있거나 붙이려고 하는 스티커의 위치에서 1이라면
            if (Map[i][j] >= 1 && M[n][r][c] == 1) {
                return false;
            }
        }
    }
 
    return true;
}
 
// 스티커 붙이기 or 떼기
void draw(int n, int a, int b, int Put)
{
    // r = i - a, c = j - b 라고 해도 된다.
    // 현재 n번 째 스티커에 대한 정보(p[n])를 활용하여 
    // 노트북에 스티커를 붙이거나(Put = 1 ~ K) 뗀다(Put = 0)
    for (int i = a, r = 0; i < a + p[n].first; i++, r++) {
        for (int j = b, c = 0; j < b + p[n].second; j++, c++) {
 
            // p[n] 활용
            if (M[n][r][c] == 1) {
                Map[i][j] = Put;
            }
        }
    }
}
 
// 스티커가 붙은 칸의 수
int calc()
{
    int ret = 0;
 
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
 
            // 1 이상일 때
            if (Map[i][j]) {
                ++ret;
            }
        }
    }
    
    return ret;
}
 
// 시계 방향 90도 회전
void rotate(int n)
{
    // 0으로 초기화
    memset(temp, 0sizeof(temp));
 
    for (int j = 0; j < p[n].second; j++) {
        for (int i = p[n].first - 1; i >= 0; i--) {
            temp[j][p[n].first - i - 1= M[n][i][j];
        }
    }
 
    for (int i = 0; i < p[n].second; i++) {
        for (int j = 0; j < p[n].first; j++) {
            M[n][i][j] = temp[i][j];
        }
    }
 
    // p[n]에서 first와 second도 바뀐다.
    int tmp = p[n].first;
    p[n].first = p[n].second;
    p[n].second = tmp;
}
 
void backtracking(int n)
{
    // 모든 스티커를 다 사용했다면
    if (n >= K) {
        // 출력하고 끝(유일한 경우)
        printf("%d\n", calc());
        exit(0); // 종료
    }
 
    bool go = true;
 
    for (int rt = 0; rt < 4 && go; rt++) {
        for (int i = 0; i < R && go; i++) {
            for (int j = 0; j < C && go; j++) {
                if (chk(n, i, j)) {
 
                    draw(n, i, j, n + 1); 
                    // n으로 하면 0이 들어가버린다. 테스트하기 쉽게 1로 표현하기 위해 n + 1을 활용,
 
                    go = false// 안 버려도 되는 스티커. 즉, 붙여지는 스티커
 
                    backtracking(n + 1);
 
                    draw(n, i, j, 0); // 다시 뗀다.
                }
 
            }
        }
 
        // 아직 안 붙여졌다면
        if (go) {
            rotate(n);
        }
    }
 
    // 회전을 다 했지만 붙여지지 않았다면
    if (go) {
        backtracking(n + 1);
    }
}
 
int main()
{
    //freopen("C:\\Users\\park7\\Desktop\\buba.in.6", "r", stdin);
    cin.tie(0);
 
    scanf("%d %d %d"&R, &C, &K);
 
    int r, c, num, idx = 1// idx는 몇 번째 스티커인지 알 수 있다. idx = 1 ~ K
    for (int d = 0; d < K; d++) {
        scanf("%d %d"&r, &c);
        p[d] = { r, c };
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                scanf("%d"&M[d][i][j]);
            }
        }
        ++idx;
    }
 
    backtracking(0);
 
    return 0;
}
cs





















728x90
반응형

'알고리즘 문제 > BOJ' 카테고리의 다른 글

1103번 게임  (0) 2020.03.25
3114번 사과와 바나나  (0) 2020.03.25
2482번 색상환  (1) 2020.03.21
1756번 피자 굽기  (0) 2020.03.20
7579번 앱  (0) 2020.03.19