백준의 색종이 붙이기(17136) 문제이다. (수정완료)

[ 문제 바로가기 ]


[ 문제풀이 ]

1) 주어진 맵을 1x1 ~ 5x5 각 5장의 색종이들로 채울 수 있는지 구하는 문제이다.

    본인은 재귀를 이용한 백트래킹 방식으로 구현해 보았다.

    재귀의 매개변수로는 어떤 변수들이 사용됬는지, 재귀를 호출하면서 더 해줘야 할 과정이 무엇이 있는지 알아보자.

   

2) 먼저, 모든 칸은 1x1로 채울 수 있다. 또한, NxN(N = 1 ~ 5)짜리 색종이로 붙일 수 있다면, 1x1 ~ N-1xN-1 짜리

    색종이로도 그 칸을 채울 수 있다. 

     위의 그림과 같이 빨강칸이 현재 점이고, 저 칸을 통해서 알아낸 결과 3x3짜리 색종이 까지 붙일 수 있다는

     결론이 나온다면, 저 현재점으로부터 1x1, 2x2칸 짜리 색종이 또한 붙일 수 있다는 의미이다.

    

     본인은 재귀 함수(본문함수명 : DFS)에는 총 7개의 매개변수를 호출해주었다.

     각 매개변수는 (현재 Idx, 사용한 1x1짜리 색종이갯수, 2x2, 3x3, 4x4, 5x5짜리 갯수, 현재까지 채운 갯수)

     이렇게 7개를 호출해 주었다.

     1x1 ~ 5x5짜리 사용한 갯수는 말 그대로이고, 현재 Idx와 현재까지 채운 갯수를 알아보자.

     본인은 맵에서 주어지는 채워야 하는 부분(맵에서 '1'의 값을 갖는 부분)의 좌표들을 Vector에 입력과 동시에

     저장해 주었다. 즉, Vector의 0번 Index부터 마지막 Index까지 1에 대한 좌표들이 저장되어 있다는 것이다.

     가장 첫 호출은, (0, 0, 0, 0, 0, 0, 0)이다. 0번 Idx부터 시작한다는 것이다.

     이 후, DFS함수 안에서는 "현재 점에서부터 채울 수 있는 색종이의 최대 크기" 를 구해온다.

     즉, 그 최대크기가 3이라면, 3x3짜리 색종이를 넣을 수 있다는 의미이다. 하지만, 위에서도 말했듯이

     3x3짜리 색종이를 넣을 수 있다고해서 그 부분에 무조건 3x3짜리 색종이가 들어간다 ! 라고 못박아 버리면

     안된다. 그 자리에는 1x1짜리도, 2x2짜리 색종이도 들어갈 수 있다는 것에 명심하자.

     최대크기를 받아온 후에는, 다시 재귀를 호출하는데 재귀를 여러번 호출해준다.

     1 ~ 최대크기 까지에 대한 호출을 해준다는 것이다.

    

     또 한가지 중요한 것이 이미 칠한 칸은 중복해서 칠하면 안된다는 것이다. 이 부분을 위해서 본인은

     Visit[][] 배열로 중복 처리를 해 주었다. 그렇다면, 이제 칠하려는 부분을 Visit배열로 어떻게 표현해줄까?

     이게 1x1이라면, 현재 좌표만 Visit[][] = true로 표시해주면 그만인데, 예를 들어서 4x4짜리 색종이를

     붙이려면 어떻게 어떻게 Visit[][] 배열에 표시를 해줘야할까??

     현재 좌표로부터 총 16칸(4x4)을 모두 true로 표시를 해줘야 한다.

     따라서 본인은 이 부분을 Make_Visit() 라는 함수를 만들어서 Visit배열을 체크해주고 해제해주도록 하였다.

    

     매개변수의 가운데에 있는 1x1 ~ 5x5 짜리 색종이를 사용한 갯수는, 내가 사용하려는 색종이를 사용할 때마다

     +1 씩 해줘서 재귀를 호출하면 된다. 그렇다면, 마지막 매개변수인 현재까지 채운 갯수는 어떻게 해줘야 할까?

     1x1 색종이를 붙일때에는 +1, 2x2 색종이를 붙일 때에는 +4, 3x3짜리 색종이를 붙일때에는 +9, ... +16 ... +25

     이런식으로 호출해주면 된다.


     그렇게되면 반드시 이런 문제가 발생할 것이다. 내가 이전에 어떤 좌표에서 특정 크기의 색종이를 붙이면서

     이미 칠해진 좌표에 DFS가 호출되는 경우가 발생할 것이다.

     예를 들어서 (0, 0)에서 2x2짜리 색종이를 붙여서 (0, 0), (0, 1), (1, 0), (1, 1) 이렇게 4좌표가 이미 칠해졌다고

     표시되어 있을 때, (0, 1)이 호출되면 어떻게 될까 ????

     이미 칠해져 있으므로 그냥 넘어가줘야 한다. 즉, 이 경우에는 DFS(Idx + 1, 그대로 ~~ ) 를 호출하게 해 주었다.

    

     재귀의 종료조건은 크게 2가지이다.

     1. 현재 사용한 색종이의 갯수가 기존의 정답 갯수보다 많아지는 경우

     -> 더 진행해봤자, 어차피 색종이만 더 나올 뿐 절대 최소값이 될 수 없는 경우이기 때문이다.

     2. 모든 칸을 다 칠한 경우

     -> 함수의 마지막 매개변수를 이용한 종료조건이다. 모든 칸을 다 칠했다면 기존의 값과 비교하여 최소값을

         갱신시켜주고 탐색을 종료하면 된다.


[ 소스코드 ]

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
#include<iostream>
#include<vector>
 
#define endl "\n"
#define MAX 10
using namespace std;
 
int Answer = 987654321, Total_Cnt;
int MAP[MAX][MAX];
bool Visit[MAX][MAX];
bool Already_Answer = true;
vector<pair<intint>> V;
 
int Min(int A, int B) { if (A < B) return A; return B; }
 
void Input()
{
    for (int i = 0; i < MAX; i++)
    {
        for (int j = 0; j < MAX; j++)
        {
            cin >> MAP[i][j];
            if (MAP[i][j] == 1)
            {
                V.push_back(make_pair(i, j));
                Total_Cnt++;
                Already_Answer = false;
            }
        }
    }
}
 
bool Can_Fill(int x, int y, int r)
{
    int Cnt = 0;
    for (int i = x; i < x + r; i++)
    {
        if (i >= MAX) break;
        for (int j = y; j < y + r; j++)
        {
            if (j >= MAX) break;
            if (MAP[i][j] == 1 && Visit[i][j] == false)
            {
                Cnt++;
            }
        }
    }
 
    if (Cnt == r * r) return true;
    else return false;
}
 
int Find_Range(int x, int y)
{
    if (Can_Fill(x, y, 2== true)
    {
        if (Can_Fill(x, y, 3== true)
        {
            if (Can_Fill(x, y, 4== true)
            {
                if (Can_Fill(x, y, 5== true)
                {
                    return 5;
                }
                return 4;
            }
            return 3;
        }
        return 2;
    }
    return 1;
}
 
void Make_Visit(int x, int y, int r, bool t)
{
    for (int i = x; i < x + r; i++)
    {
        for (int j = y; j < y + r; j++)
        {
            Visit[i][j] = t;
        }
    }
}
 
void DFS(int Idx, int One, int Two, int Three, int Four, int Five, int Total)
{
    int Use = One + Two + Three + Four + Five;
 
    if (Answer < Use) return;
    if (Total == Total_Cnt)
    {
        Answer = Min(Answer, Use);
        return;
    }
    if (Visit[V[Idx].first][V[Idx].second] == true)
    {
        DFS(Idx + 1, One, Two, Three, Four, Five, Total);
        return;
    }
 
    int Can_Fill = Find_Range(V[Idx].first, V[Idx].second);
    if (Can_Fill == 1)
    {
        if (One + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 1true);
            DFS(Idx + 1, One + 1, Two, Three, Four, Five, Total + 1);
            Make_Visit(V[Idx].first, V[Idx].second, 1false);
        }
    }
    else if (Can_Fill == 2)
    {
        if (Two + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 2true);
            DFS(Idx + 1, One, Two + 1, Three, Four, Five, Total + 4);
            Make_Visit(V[Idx].first, V[Idx].second, 2false);
        }
 
        if (One + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 1true);
            DFS(Idx + 1, One + 1, Two, Three, Four, Five, Total + 1);
            Make_Visit(V[Idx].first, V[Idx].second, 1false);
        }
    }
    else if (Can_Fill == 3)
    {
        if (Three + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 3true);
            DFS(Idx + 1, One, Two, Three + 1, Four, Five, Total + 9);
            Make_Visit(V[Idx].first, V[Idx].second, 3false);
        }
 
        if (Two + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 2true);
            DFS(Idx + 1, One, Two + 1, Three, Four, Five, Total + 4);
            Make_Visit(V[Idx].first, V[Idx].second, 2false);
        }
 
        if (One + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 1true);
            DFS(Idx + 1, One + 1, Two, Three, Four, Five, Total + 1);
            Make_Visit(V[Idx].first, V[Idx].second, 1false);
        }    
    }
    else if (Can_Fill == 4)
    {
        if (Four + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 4true);
            DFS(Idx + 1, One, Two, Three, Four + 1, Five, Total + 16);
            Make_Visit(V[Idx].first, V[Idx].second, 4false);
        }
 
        if (Three + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 3true);
            DFS(Idx + 1, One, Two, Three + 1, Four, Five, Total + 9);
            Make_Visit(V[Idx].first, V[Idx].second, 3false);
        }
 
        if (Two + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 2true);
            DFS(Idx + 1, One, Two + 1, Three, Four, Five, Total + 4);
            Make_Visit(V[Idx].first, V[Idx].second, 2false);
        }
 
        if (One + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 1true);
            DFS(Idx + 1, One + 1, Two, Three, Four, Five, Total + 1);
            Make_Visit(V[Idx].first, V[Idx].second, 1false);
        }
    }
    else if (Can_Fill == 5)
    {
        if (Five + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 5true);
            DFS(Idx + 1, One, Two, Three, Four, Five + 1, Total + 25);
            Make_Visit(V[Idx].first, V[Idx].second, 5false);
        }
 
        if (Four + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 4true);
            DFS(Idx + 1, One, Two, Three, Four + 1, Five, Total + 16);
            Make_Visit(V[Idx].first, V[Idx].second, 4false);
        }
 
        if (Three + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 3true);
            DFS(Idx + 1, One, Two, Three + 1, Four, Five, Total + 9);
            Make_Visit(V[Idx].first, V[Idx].second, 3false);
        }
 
        if (Two + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 2true);
            DFS(Idx + 1, One, Two + 1, Three, Four, Five, Total + 4);
            Make_Visit(V[Idx].first, V[Idx].second, 2false);
        }
 
        if (One + 1 <= 5)
        {
            Make_Visit(V[Idx].first, V[Idx].second, 1true);
            DFS(Idx + 1, One + 1, Two, Three, Four, Five, Total + 1);
            Make_Visit(V[Idx].first, V[Idx].second, 1false);
        }        
    }
}
 
void Solution()
{
    if (Already_Answer == true)
    {
        cout << 0 << endl;
        return;
    }
    
    DFS(0000000);
    if (Answer == 987654321) Answer = -1;
    cout << Answer << endl;
}
 
void Solve()
{
    Input();
    Solution();
}
 
int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
 
    //freopen("Input.txt", "r", stdin);
    Solve();
 
    return 0;
}
cs



+ Recent posts