문제에서 어떻게 풀라고 다 알려주는 구현문제보다도, 이 문제가 완전탐색이구나 깨닫는 것은 아직 잘 감이 안온다. 이틀을 붙잡고 있다가 해설을 봤다. (그렇게 어려운 문제가 아니었음에도!!)
이 문제는 가능한 높이 경우가 0~256 사이의 자연수로 모든 높이의 경우에 대해 완전탐색을 해도 된다.
0~256 사이의 높이 curr_h
에 대해서 모든 블럭의 높이를 살펴보며 그 높이에 맞추려면 1. 쌓아야 하는지 2. 깎아야 하는지 체크해주면 된다.
use
변수에 쌓아야 하는 블록의 수를 더해준다.save
변수에 깎아야 하는 블록의 수를 더해준다.만약 사용한 블록의 수 (use
) 가 사용 가능한 전체 블록의 수 (B + save
)보다 많다면 그 경우는 체크하지 않는다.
블록을 쌓는 경우에는 2초 소요, 블록을 깎는 경우에는 1초가 소요되므로 전체 소요되는 시간은 use + save * 2
가 된다. 만약 가능한 경우가 여러개 있다면 가장 높은 땅의 높이를 출력해야 하므로 최댓값이 갱신될 때마다 땅의 높이도 함께 갱신해준다.
각 블록의 높이를 2차원 배열로 받아 N^3의 시간복잡도를 사용하면 시간초과가 날 수 있다. 따라서 블록의 좌표값은 따로 고려할 사항이 아니므로 높이별로 블록의 개수를 저장해주는 딕셔너리를 사용했다.
아래 코드는 python3로 통과한다.
from sys import stdin
input = stdin.readline
N, M, B = map(int, input().split(" "))
heights = {}
for _ in range(N):
data = list(map(int, input().split(" ")))
# 해당 높이의 블록 갯수 세기
for i in data:
heights[i] = heights.get(i, 0) + 1
answer = int(1e9)
ground_height = 0
# 깎아서 저장한 블록의 수 : save
# 인벤에서 가져와 쌓은 블록의 수 : use
for curr_h in range(257):
save = 0
use = 0
for height in heights:
# 블록 쌓기
if height < curr_h:
use += (curr_h - height) * heights[height]
# 블록 깎기
else:
save += (height - curr_h) * heights[height]
# 깎은 블록의 수가 사용 가능한 블록의 수보다 크다면 고려X
if use > save + B:
continue
time = save * 2 + use
if time <= answer:
answer = time
ground_height = curr_h
print(answer, ground_height)
DFS, BFS로 풀었다가 시간 초과도 나고, 어떻게 접근해야될지도 모르겠어서 1시간정도 고민하고 해설을 찾아봤다. 오늘 푸는 문제 다 해설 보는 것 같다... 슬프다.
주의할 점은 지나가는 칸은 방문한 것X, 최종 이동을 마친 칸만 방문O이라는 점이다!
이 문제는 가로, 세로 길이에 따라 최적의 경우가 정해져 있다. (그리디)
from sys import stdin
input = stdin.readline
N, M = map(int, input().split(" ")) # 세로 길이 N (row) 가로길이 M (col)
answer = 0
if N == 1:
answer = 1
elif N == 2:
if 1 <= M <= 6:
answer = (M + 1) // 2
else:
answer = 4
elif N >= 3:
if 1 <= M <= 6:
answer = min(M, 4)
else:
answer = M - 2
print(answer)
어려운 문제가 이렇게 간결하게 최종 코드로 완성되면 알고리즘 공부를 열심히 해야겠다고 느낀다. 이 문제가 실버 3단계정도라니....
삼성 SW 역량 테스트 기출 문제다. 삼성 문제는 정말 문제에서 시키는대로 차근차근 구현하면 된다.
BFS 방식을 사용하기 위해 deque
를 사용했다.
1. 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
-> 살펴봐야 하는 칸을 큐에서 꺼내 값이 0(청소X칸)인 경우 2로 변경 (청소O 칸)
벽과 구분해주기 위해 값 2를 사용했다
2. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우,
-> is_unclean
플래그를 사용해서 체크
2-1. 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로 돌아간다.
-> 후진하는 방법은 현재 방향에서 -2를 빼준 뒤 4로 나눈 나머지로 계산 (북쪽을 바라보고 있는 경우, 남쪽으로 이동하면 된다)
-> 큐에 좌표값 삽입
2-2. 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.
-> return으로 함수 빠져나가기
3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우,
-> is_unclean
플래그가 참이라면
3-1. 반시계 방향으로 90도 회전한다.
-> d -= 1, d %= 4
3-2. 바라보는 방향을 기준으로 앞쪽 칸이 청소되지 않은 빈 칸인 경우 한 칸 전진한다.
-> 만약 조건에 해당하지 않으면 계속 회전시킨다.
3-3. 1번으로 돌아간다.
-> 큐에 좌표값 삽입
from sys import stdin
from collections import deque
input = stdin.readline
N, M = map(int, input().split(" ")) # row : N, col : M
R, C, d = map(int, input().split(" "))
graph = [list(map(int, input().split(" "))) for _ in range(N)]
# d : 0 북 / 1 동 / 2 남 / 3 서
dr = [-1, 0, 1, 0]
dc = [0, 1, 0, -1]
def clean(q):
global N, M, d
while q:
r, c = q.popleft()
# 1. 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
if graph[r][c] == 0:
graph[r][c] = 2
is_unclean = False
for i in range(4):
nr = r + dr[i]
nc = c + dc[i]
if 0 <= nr < N and 0 <= nc < M and graph[nr][nc] == 0:
is_unclean = True
break
# 2. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우
if is_unclean:
d -= 1
d %= 4
nr = r + dr[d]
nc = c + dc[d]
while graph[nr][nc] != 0:
d -= 1
d %= 4
nr = r + dr[d]
nc = c + dc[d]
q.append((nr, nc))
# 3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우
else:
# 한칸 후진용 임시 방향 temp_d
temp_d = d
temp_d += 2
temp_d %= 4
nr = r + dr[temp_d]
nc = c + dc[temp_d]
# 한 칸 후진할 수 있다면
if graph[nr][nc] != 1:
q.append((nr, nc))
# 뒤쪽 칸이 벽이면 작동 멈추기
else:
return
q = deque([(R, C)])
clean(q)
answer = 0
for r in range(N):
for c in range(M):
if graph[r][c] == 2:
answer += 1
print(answer)
프로그래머스 문제를 풀어 따로 게시물을 작성했다.