본문 바로가기
알고리즘

[백준] 로봇청소기 [Gold 5, 코드 + 해설]

by itstime0809 2023. 7. 1.
728x90

구현 & 시뮬레이션

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

 

14503번: 로봇 청소기

첫째 줄에 방의 크기 $N$과 $M$이 입력된다. $(3 \le N, M \le 50)$  둘째 줄에 처음에 로봇 청소기가 있는 칸의 좌표 $(r, c)$와 처음에 로봇 청소기가 바라보는 방향 $d$가 입력된다. $d$가 $0$인 경우 북쪽

www.acmicpc.net

 

코드

import sys 
input = sys.stdin.readline

# 방의 크기를 입력받는다.
n, m = map(int, input().split())
# 좌표, 방향이 입력된다.
r, c, d = map(int, input().split())

# 탐색할 맵을 입력 받는다.
board = [input().split() for _ in range(n)]

# 북, 서, 남, 동 순으로 전진과 후진을 컨트롤할 맵을 만들어주자
# d = 0 북
# d = 1 동
# d = 2 남
# d = 3 서
control = {0: [(-1, 0), (1, 0)], 3: [(0, -1), (0, 1)], 2: [(1, 0), (-1, 0)], 1: [(0, 1), (0, -1)]} 


cnt = 0
# 북 동 남 서
dx = [0, 1, 2, 3]
while True:
	# 현재칸이 청소되지 않은 경우, 현재칸을 청소한다.
	if board[r][c] != "X":
		board[r][c] = "X"
		cnt += 1
		
		# 청소되지 않은 빈칸이 없는 경우
		if board[r-1][c] != "0" and board[r][c-1] != "0" and board[r+1][c] != "0" and board[r][c+1] != "0":
			# 바라보는 방향을 기준으 한칸 후진할 수 있다면
			if board[r+control[d][1][0]][c+control[d][1][1]] == "X" or board[r+control[d][1][0]][c+control[d][1][1]] == '0': 
				r, c = r+control[d][1][0], c+control[d][1][1]
				continue
			elif board[r+control[d][1][0]][c+control[d][1][1]] == '1':
				break

		# 청소되지 않은 빈칸이 있는 경우
		elif board[r-1][c] == "0" or board[r][c-1] == "0" or board[r+1][c] == "0" or board[r][c+1] == "0":
			# 모두가 청소가 되어져 있지 않은 경우 즉 빈칸이 존재하는 경우. (하나라도)
			# 현재 방향의 인덱스를 가지고 와서 반시계 방향으로 회전해야하니까
            
			d = dx[dx.index(d)-1 % len(dx)]
			# 방향을 바꾼뒤 현재 방향을 기준으로 
			# 앞쪽 칸이라고 하면 전진하는 칸을 의미한다.
			# 전진은 청소되지 않은 빈칸 0이라면 좌표를 갱신해준다.
            
			if board[r+control[d][0][0]][c+control[d][0][1]] == '0':
				r, c = r+control[d][0][0], c+control[d][0][1]
                
	# 돌아갔는데 현재 칸이 청소가 되어 있다면
	else:
		# 현재 칸의 주변 4칸중 청소되지 않은 빈칸이 있나 없나를 보는거야
		if board[r-1][c] != "0" and board[r][c-1] != "0" and board[r+1][c] != "0" and board[r][c+1] != "0":
			# 바라보는 방향을 기준으 한칸 후진할 수 있다면
			if board[r+control[d][1][0]][c+control[d][1][1]] == "X" or board[r+control[d][1][0]][c+control[d][1][1]] == '0': 
				r, c = r+control[d][1][0], c+control[d][1][1]
				continue

			elif board[r+control[d][1][0]][c+control[d][1][1]] == '1':
				break

		# 청소되지 않은 빈칸이 있는 경우
		elif board[r-1][c] == "0" or board[r][c-1] == "0" or board[r+1][c] == "0" or board[r][c+1] == "0":
			
			d = dx[dx.index(d)-1 % len(dx)]

			if board[r+control[d][0][0]][c+control[d][0][1]] == '0':
				r, c = r+control[d][0][0], c+control[d][0][1]

print(cnt)

 

설명

골드 문제를 처음 접해보며 풀어보았는데 조건대로 구현하면 되는 문제이지만 '방향에 따라 전진과 후진을 다르게 생각해줘야 한다는 점'에서는 실버 문제랑 약간의 차이가 있는 듯했다. 그렇기 때문에 기존 시뮬레이션 문제에서 접했던 방법으로는 방향을 정해 해당 방향으로 이동하는 방법에 대한 정의가 하나만 있었지만 해당 문제 같은 경우 '방향에 대해서 전진, 후진' 둘 다 생각해주어야 하는 문제이기 때문에 문제의 조건을 꼼꼼히 읽는 게 중요했던 문제다. 또한 문제에서는 한 가지 경우만 알려주고 있다.

 

 

"청소되지 않은 경우 현재 칸을 청소 한다."

 

 

 사실 위 조건만 잘 생각해 보면 나머지는 조건을 구현하는데 어려움이 있었지 이해하는 것은 크게 어렵지 않았다. 우선 청소되지 않은 경우 현재 칸을 청소한다는 건 현재 칸이 청소가 되어 있지 않은 '0'인 상태라는 걸 의미한다. 문제에서는 '처음에는 항상 빈칸이다.'라고 명시해 두었기 때문에 처음부터 청소해야 되지 않은 빈칸일 경우는 없는 것이다. 나머지는 구현 영역이기 때문에 해당 조건에 반대되는 경우만 설명해 보자면 문제 조건에서는 1번으로 돌아가라는 말을 한다. 1번으로 돌아가는 경우는 위와 같이 청소되지 않은 경우 현재 칸을 청소하는 경우다. 그렇다면 '만약에 1번으로 돌아갔는데 현재 칸이 청소가 되어 있다면?' 이런 경우 문제가 발생하게 된다. 왜냐하면 현재 칸으로 돌아갔는데 청소가 되어 있는 경우에 대한 분기는 따로 존재하지 않는다면 처리가 되지 않기 때문이다. 따라서 위 조건에 대한 반대 조건을 생각했어야 했다.

 

 두 번째는 '전진과 후진'이다. 전진과 후진에 대한 정의는 문제를 읽어보고 펜으로 직접 몇 번 해본다면 충분히 전진은 어떻게 해야 가능한지 어떤 방향에 대해서 전진과 후진이 어떻게 일어나는지를 알 수 있다. 하지만 하나 더 알고 있어야 하는 것은 청소되지 않은 빈칸에 대해서 카운트를 세는 거지 전진과 후진에 문제에서 청소가 된 곳을 카운트할 필요는 없다는 것이다. 이 두 가지만 조심해서 푼다면 구현에 시간이 걸릴지 몰라도 이해하는 데에는 어려움이 없었다고 생각했다. 그리고 위 코드에서는 반복적인 코드가 존재하기 때문에 함수화 해서 약간의 수정만 거치게 되면 코드를 두번 작성하지 않아도 된다.