connecting the dots

[BOJ/Java] 14503 : 로봇 청소기 본문

algorithm/BOJ

[BOJ/Java] 14503 : 로봇 청소기

林 : 2021. 2. 8. 06:04

문제

www.acmicpc.net/problem/14503

 

14503번: 로봇 청소기

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어

www.acmicpc.net

 

풀이

사실 문제 이해가 안 되서 애를 먹었다 .. 내 국어능력이 이정도였나 싶었다 .. 문제만 제대로 이해한다면 구현은 생각보다 어렵지 않은 문제다. 정말 딱 시키는대로만 하면 되는.

내가 문제를 제대로 이해하지 못해서 접근을 잘못했던 것은 다음과 같다.

  • 청소를 이미 완료했어도 해당 구역을 지나갈 수는 있다는 점 / '이미 청소한 곳은 다시 청소하지 않는다'는 의미를 '이미 청소한 곳은 다시 지나가지 않는다'로 받아들였다. 후진을 통해 이미 청소한 곳을 지나갈 수는 있고, 다시 청소를 하지는 않기 때문에 청소한 곳에 대한 count는 변화가 없어야 한다.
  • '네 방향 모두 청소가 이미 되어있거나 벽인 경우에는, 바라보는 방향을 유지한 채로 한 칸 후진을 하고 2번으로 돌아간다' 라는 설명을 보고 후진을 2칸 한다고 마음대로 읽어버렸다 .. 왜 그랬는지는 정말 모르겠지만 .. 문제를 꼼꼼히 읽자 제발

 

 

이렇게 구현할 계획을 짜보았다. area라는 static class를 만들어주고 해당 위치가 벽인지 확인할 수 있는 value와 청소 여부를 확인하는 boolean type의 clean변수를 만들어주었다.

재귀는 사용하지 않고 while문을 통해 구현했고 청소기가 더이상 후진할 곳이 없으면 while문을 빠져나오도록 했다. 

그리고 평소에 방향을 전환할 때 turn()이라는 메소드를 만들어서 static 선언된 현재 방향을 input으로 받으면 현재 방향에 따라 방향을 바꿔주도록 구현하는 편이었는데, 이번엔 방향 전환 배열 인덱스를 살펴보고 다음 방향의 인덱스는 (x + 3) % 4 임을 알아내 적용했다. 이 방법을 사용하니 따로 함수를 만들어주지 않아도 돼서 편리했다!

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static int[][] dir = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; // 북 동 남 서
	static int d;
	static int impossible;
	static int backX, backY;

	public static void back(int d) {
		if (d == 0) {
			backX = dir[2][0];
			backY = dir[2][1];
		} else if (d == 1) {
			backX = dir[3][0];
			backY = dir[3][1];
		} else if (d == 2) {
			backX = dir[0][0];
			backY = dir[0][1];
		} else if (d == 3) {
			backX = dir[1][0];
			backY = dir[1][1];
		}

	}

	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(in.readLine(), " ");
		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());

		st = new StringTokenizer(in.readLine(), " ");
		int r = Integer.parseInt(st.nextToken());
		int c = Integer.parseInt(st.nextToken());
		d = Integer.parseInt(st.nextToken()); // 0:북 , 1:동, 2:남. 3:서

		area[][] map = new area[n][m];

		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(in.readLine(), " ");
			for (int j = 0; j < m; j++) {
				map[i][j] = new area(Integer.parseInt(st.nextToken()), false);
				// System.out.print(map[i][j].value + " ");
			}

			// System.out.println();
		}

		int[][] check = new int[n][m];

		int cnt = 0;
		while (true) {

			if (!map[r][c].clean) {
				check[r][c] = ++cnt;
			}

			map[r][c].clean = true; // 현재 위치를 청소

			for (int i = 0; i < 4; i++) { // 사방 탐색

				d = (d + 3) % 4;
				int dx = r + dir[d][0];
				int dy = c + dir[d][1];

				if (dx < 0 || dy < 0 || dx >= n || dy >= m) {
					impossible++;
					continue; // 범위 확인
				}
				if (map[dx][dy].value != 1 && map[dx][dy].clean) { // 탐색한 방이 청소가 되어있다면
					impossible++;
				} else if (map[dx][dy].value != 1 && !map[dx][dy].clean) { // 탐색한 방이 청소가 안 되어있다면
					r = dx;
					c = dy;
					break;
				} else if (map[dx][dy].value == 1) {
					impossible++; // 탐색한 곳이 벽이라면
				}

			}

			if (impossible == 4) { // 탐색이 끝난 후, 네 방면 모두 불가능하다면
				back(d); // 후진 방향 설정
				int dx_b = r + backX;
				int dy_b = c + backY;
				if (dx_b < 0 || dy_b < 0 || dx_b >= n || dy_b >= m) {
					break;
				}
				r = r + backX;
				c = c + backY;
				if (map[r][c].value == 1)
					break;
			}

			impossible = 0;

		}
		System.out.println(cnt);

	}

	static class area {
		int value;
		boolean clean;

		area(int value, boolean clean) {
			this.value = value;
			this.clean = clean;
		}

	}
}

 

느낀점

  • 사이트에 나온 예제는 정상적으로 작동하고, 로봇청소기의 이동경로도 정확한데 백준에 제출하니 틀렸습니다가 떴다 .. 이유를 찾지 못하고 이것저것 시도해보느라 많은 시간을 허비했다. 알고보니 d로 받았던 로봇청소기의 처음 방향을 d = 0 으로 초기화하고 while문을 돌린 거였다^^ 진짜 어이없고 허탈했다. 처음 코드를 작성할 때 정말 신중하게 작성해야 한다는 걸 느꼈다.. 내 코드를 다시 뜯어보면서 오류를 잡아내는 건 정말 어렵다 ㅠ_ㅠ 이거 찾다가 벌써 6시 됐다 ..