DFS를 이용하여 전체 탐색을 하는 문제.
잘못된 구상으로 1번 헤맸고, 3번 틀렸음.
처음 표를 보고, 다음과 같은 생각이 들었다.
이걸 인접 행렬로 생각해보는 건 어떨까?
그래서 다음과 같은 행렬을 가지고 그래프를 그려보았다.
그런데 이렇게 되면, 2번을 제외하고 나머지 지점을 고르면 모든 지점을 전부 방문하게 되어버리므로 정확한 답을 얻을 수가 없다.
그래서 한 발짝 물러나서 이렇게 접근해보기로 했다.
모든 지점을 전부 방문하긴 방문해야 하니, 네 방향을 전부 들르는 DFS 탐색을 구현해보자.
위 생각을 바탕으로 짠 첫 코드다.
#include <stdio.h>
int table[25][25];
int visited[25][25];
int count[25][25];
int N = 0;
int visit(int x, int y, int count) {
if (table[x][y] == 0)
return count;
if (x < N - 1 && table[x + 1][y] != 0 && visited[x + 1][y] != 1) {
visited[x + 1][y] = 1;
count += visit(x + 1, y, 1);
}
if (x > 1 && table[x - 1][y] != 0 && visited[x - 1][y] != 1) {
visited[x - 1][y] = 1;
count += visit(x - 1, y, 1);
}
if (y < N - 1 && table[x][y + 1] != 0 && visited[x][y + 1] != 1) {
visited[x][y + 1] = 1;
count += visit(x, y + 1, 1);
}
if (y > 1 && table[x][y - 1] != 0 && visited[x][y - 1] != 1) {
visited[x][y - 1] = 1;
count += visit(x, y - 1, 1);
}
return count + 1;
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
char input = getchar();
if (input == '0')
table[i][j] = 1;
else if (input == '1')
table[i][j] = 0;
}
getchar();
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
int count = visit(x, y, 0);
printf("%d\n", count);
}
}
return 0;
}
이 코드에는 치명적인 문제가 있었다. 이는 다음 문단에서 후술하겠다.
문제가 있었던만큼 당연히 작동하지 않았고, 이 때 시간이 없어서 푸는 것을 잠시 미뤘었다.
#include <stdio.h>
int table[25][25];
int visited[25][25];
int N = 0;
int dfs(int x, int y, int count) {
if (x < 0 || x > N || y < 0 || y > N || table[x][y] == 0 ||
visited[x][y] == 1)
return count;
printf("(%d,%d) : %d\n", x, y, count);
visited[x][y] = 1;
return dfs(x - 1, y, count + 1) + dfs(x + 1, y, count + 1) +
dfs(x, y - 1, count + 1) + dfs(x, y + 1, count + 1);
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
char input = getchar();
if (input == '0')
table[i][j] = 1;
else if (input == '1')
table[i][j] = 0;
}
getchar();
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
int count = dfs(x, y, 0);
printf("%d\n", count);
}
}
return 0;
}
dfs()
코드를 조금 다듬어서, 4방향으로 각각 탐색할 때 인접한 타일 개수를 더한 값을 반환하도록 했다.
이미 방문한 곳이라면 집계가 되지 않고 멈출 것이므로, 원리 자체만 두고 보면 괜찮았지만, 문제는 이전 갯수를 그대로 활용한 점이었다.
count + 1
이 들어간 결과, 결국 count가 늘어났든 말든 각 재귀마다 4씩 더해지게 되어 이 또한 제대로 된 결과를 내지 못한다. 따라서 이 코드를 또 다시 고칠 필요가 있었다.
#include <stdio.h>
int table[25][25];
int visited[25][25];
int N = 0;
int dfs(int x, int y, int count) {
if (x < 0 || x > N || y < 0 || y > N || table[x][y] == 0 ||
visited[x][y] == 1)
return count;
printf("(%d,%d) : %d\n", x, y, count);
visited[x][y] = 1;
return dfs(x - 1, y, count + 1) + dfs(x + 1, y, count + 1) +
dfs(x, y - 1, count + 1) + dfs(x, y + 1, count + 1);
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
char input = getchar();
if (input == '0')
table[i][j] = 0;
else if (input == '1')
table[i][j] = 1;
}
getchar();
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
int count = dfs(x, y, 0);
printf("%d\n", count);
}
}
return 0;
}
앞서 말했던 '치명적 실수'가 뭔지 알아차렸는가? 멍청하게도 '0' 일때 1을 넣었고 '1' 일때 0을 넣었다. 이 부분을 고쳤다.
#include <stdio.h>
int table[25][25];
int visited[25][25];
int N = 0;
int dfs(int x, int y, int count) {
if (x < 0 || x > N || y < 0 || y > N || table[x][y] == 0 ||
visited[x][y] == 1)
return count;
printf("(%d,%d) : %d\n", x, y, count);
visited[x][y] = 1;
return dfs(x - 1, y, count + 1) + dfs(x + 1, y, count + 1) +
dfs(x, y - 1, count + 1) + dfs(x, y + 1, count + 1);
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%1d", &table[i][j]);
}
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
int count = dfs(x, y, 0);
printf("%d\n", count);
}
}
return 0;
}
getchar()
로 입력받는 부분에서 뭔가 찜찜한 느낌이 들어 printf
함수로 제대로 입력을 받았는지 확인했는데, 역시나 제대로 받지 못하고 있었다. 시중에 있는 답안을 참고한 결과 scanf("%1d")
를 이용하면 딱 한 문자만 받을 수 있다는 걸 알게 되어 바로 적용했다.
#include <stdio.h>
#include <stdlib.h>
#define MAX(a, b) a > b ? a : b
int table[25][25];
int visited[25][25];
int count[313];
int N = 0;
int apartCount = 0;
int compare(const void *a, const void *b) {
if (*(int *)a > *(int *)b)
return 1;
else if (*(int *)a < *(int *)b)
return -1;
else
return 0;
}
int dfs(int x, int y, int count) {
if (x < 0 || x > N || y < 0 || y > N || table[x][y] == 0 ||
visited[x][y] == 1)
return count;
visited[x][y] = 1;
count++;
count = MAX(count, dfs(x + 1, y, count));
count = MAX(count, dfs(x - 1, y, count));
count = MAX(count, dfs(x, y - 1, count));
count = MAX(count, dfs(x, y + 1, count));
return count;
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%1d", &table[i][j]);
}
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
int c = dfs(x, y, 0);
printf("c=%d\n", c);
if (c > 0)
count[apartCount++] = c;
}
}
qsort(count, apartCount, sizeof(int), compare);
printf("%d\n", apartCount);
for (int i = 0; i < apartCount; i++) {
printf("%d\n", count[i]);
}
return 0;
}
4차 수정까지는 완전한 코드가 아니었다. 정렬하는 부분도 빠져 있고, 총 단지 개수를 구하는 부분도 빠져 있었다.
기존에 더하는 부분을, count
라는 매개변수의 원래 값과 dfs()
의 값의 최대값을 비교하는 부분으로 바꿨다. 만약에 인접한 아파트 단지가 있다면 dfs()
값은 항상 count
보다 클 것이므로, 최댓값을 이용한 대입이 먹힐 것으로 생각했다.
그런데, 이게 맞기를 바랬지만 '틀렸습니다'를 받았다. 무엇이 잘못되었는지를 한번 살펴보자.
#include <stdio.h>
#include <stdlib.h>
#define MAX(a, b) a > b ? a : b
int table[25][25];
int visited[25][25];
int count[315];
int maxCount = 0;
int N = 0;
int apartCount = 0;
int compare(const void *a, const void *b) {
if (*(int *)a > *(int *)b)
return 1;
else if (*(int *)a < *(int *)b)
return -1;
else
return 0;
}
void dfs(int x, int y) {
if (x < 0 || x > N || y < 0 || y > N || table[x][y] == 0 ||
visited[x][y] == 1)
return;
visited[x][y] = 1;
maxCount++;
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y - 1);
dfs(x, y + 1);
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%1d", &table[i][j]);
}
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
maxCount = 0;
dfs(x, y);
if (maxCount > 0)
count[apartCount++] = maxCount;
}
}
qsort(count, apartCount, sizeof(int), compare);
printf("%d\n", apartCount);
for (int i = 0; i < apartCount; i++) {
printf("%d\n", count[i]);
}
return 0;
}
gdb
까지 동원하여 count
의 변화를 살펴보았는데, 이상하게도 dfs()의 값이 count보다 더 커서 대입될 것이라 생각했던 부분에서 count의 값이 계속해서 대신 대입됐다. 최댓값 자체는 정확하게 구하는데, 재귀로 타고 내려갔던 길을 다시 올라가는 과정에서 문제가 발생한 듯 싶었다.
이 부분을 하루 종일 붙잡기에는 내 인내심이 조금씩 바닥나고 있었기 때문에, 전역변수로 따로 빼둔 다음 dfs()
호출 전에 0으로 초기화시켰다. 이래놓고 나니 전형적인 백트래킹 문제가 되어버렸다.
그런데 이 코드 또한 내 발목을 잡았다. 도대체 무엇이 문제였던 것일까?
#include <stdio.h>
#include <stdlib.h>
#define MAX(a, b) a > b ? a : b
int table[25][25];
int visited[25][25];
int count[315];
int maxCount = 0;
int N = 0;
int apartCount = 0;
int compare(const void *a, const void *b) {
if (*(int *)a > *(int *)b)
return 1;
else if (*(int *)a < *(int *)b)
return -1;
else
return 0;
}
void dfs(int x, int y) {
if (x < 0 || x >= N || y < 0 || y >= N || table[x][y] == 0 ||
visited[x][y] == 1)
return;
visited[x][y] = 1;
maxCount++;
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y - 1);
dfs(x, y + 1);
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
scanf("%1d", &table[i][j]);
}
}
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++) {
maxCount = 0;
dfs(x, y);
if (maxCount > 0)
count[apartCount++] = maxCount;
}
}
qsort(count, apartCount, sizeof(int), compare);
printf("%d\n", apartCount);
for (int i = 0; i < apartCount; i++) {
printf("%d\n", count[i]);
}
return 0;
}
그랬다. x > N || y > N
이 아니라 x >= N || y >= N
이었던 것이다!
전자의 경우에는 N = 4일 때 x = 4인 경우까지 고려하므로 틀릴 수가 있었다. 이런! 경계 조건을 잘못 본 탓에 거의 다 맞은 문제를 틀려버렸다.
scanf()
의 또다른 활용법%Nd
을 이용하자(ex. %1d
).