[프로그래머스] 경주로 건설
Updated:
문제 설명
건설회사의 설계사인 죠르디
는 고객사로부터 자동차 경주로 건설에 필요한 견적을 의뢰받았습니다.
제공된 경주로 설계 도면에 따르면 경주로 부지는 N x N
크기의 정사각형 격자 형태이며 각 격자는 1 x 1
크기입니다.
설계 도면에는 각 격자의 칸은 0
또는 1
로 채워져 있으며, 0
은 칸이 비어 있음을 1
은 해당 칸이 벽으로 채워져 있음을 나타냅니다.
경주로의 출발점은 (0, 0) 칸(좌측 상단)이며, 도착점은 (N-1, N-1) 칸(우측 하단)입니다. 죠르디는 출발점인 (0, 0) 칸에서 출발한 자동차가 도착점인 (N-1, N-1) 칸까지 무사히 도달할 수 있게 중간에 끊기지 않도록 경주로를 건설해야 합니다.
경주로는 상, 하, 좌, 우로 인접한 두 빈 칸을 연결하여 건설할 수 있으며, 벽이 있는 칸에는 경주로를 건설할 수 없습니다.
이때, 인접한 두 빈 칸을 상하 또는 좌우로 연결한 경주로를 직선 도로
라고 합니다.
또한 두 직선 도로
가 서로 직각으로 만나는 지점을 코너
라고 부릅니다.
건설 비용을 계산해 보니 직선 도로
하나를 만들 때는 100원이 소요되며, 코너
를 하나 만들 때는 500원이 추가로 듭니다.
죠르디는 견적서 작성을 위해 경주로를 건설하는 데 필요한 최소 비용을 계산해야 합니다.
예를 들어, 아래 그림은 직선 도로
6개와 코너
4개로 구성된 임의의 경주로 예시이며, 건설 비용은 6 x 100 + 4 x 500 = 2600원 입니다.
또 다른 예로, 아래 그림은 직선 도로
4개와 코너
1개로 구성된 경주로이며, 건설 비용은 4 x 100 + 1 x 500 = 900원 입니다.
도면의 상태(0은 비어 있음, 1은 벽)을 나타내는 2차원 배열 board가 매개변수로 주어질 때, 경주로를 건설하는데 필요한 최소 비용을 return 하도록 solution 함수를 완성해주세요.
제한사항
- board는 2차원 정사각 배열로 배열의 크기는 3 이상 25 이하입니다.
- board 배열의 각 원소의 값은 0 또는 1 입니다.
- 도면의 가장 왼쪽 상단 좌표는 (0, 0)이며, 가장 우측 하단 좌표는 (N-1, N-1) 입니다.
- 원소의 값 0은 칸이 비어 있어 도로 연결이 가능함을 1은 칸이 벽으로 채워져 있어 도로 연결이 불가능함을 나타냅니다.
- board는 항상 출발점에서 도착점까지 경주로를 건설할 수 있는 형태로 주어집니다.
- 출발점과 도착점 칸의 원소의 값은 항상 0으로 주어집니다.
입출력 예
board | result |
---|---|
[[0,0,0],[0,0,0],[0,0,0]] | 900 |
[[0,0,0,0,0,0,0,1],[0,0,0,0,0,0,0,0],[0,0,0,0,0,1,0,0],[0,0,0,0,1,0,0,0],[0,0,0,1,0,0,0,1],[0,0,1,0,0,0,1,0],[0,1,0,0,0,1,0,0],[1,0,0,0,0,0,0,0]] | 3800 |
[[0,0,1,0],[0,0,0,0],[0,1,0,1],[1,0,0,0]] | 2100 |
[[0,0,0,0,0,0],[0,1,1,1,1,0],[0,0,1,0,0,0],[1,0,0,1,0,1],[0,1,0,0,0,1],[0,0,0,0,0,0]] | 3200 |
입출력 예 설명
입출력 예 #1
본문의 예시와 같습니다.
입출력 예 #2
위와 같이 경주로를 건설하면 직선 도로
18개, 코너
4개로 총 3800원이 듭니다.
입출력 예 #3
위와 같이 경주로를 건설하면 직선 도로
6개, 코너
3개로 총 2100원이 듭니다.
입출력 예 #4
붉은색 경로와 같이 경주로를 건설하면 직선 도로
12개, 코너
4개로 총 3200원이 듭니다.
만약, 파란색 경로와 같이 경주로를 건설한다면 직선 도로
10개, 코너
5개로 총 3500원이 들며, 더 많은 비용이 듭니다.
풀이과정
풀이 1
bfs를 활용하여 풀어보았는데, 시간초과로 67.7점 나왔다. 보완이 필요. answer를 이용하여 가지치기를 하였는데도 시간 초과가 뜬다. 테스트 케이스 10, 11, 12
from collections import deque
def solution(board):
answer = int(1e9)
n = len(board)
q = deque()
q.append(((0,0),(0,0), [(0,0)]))
dx = [0,1,0,-1]
dy = [1,0,-1,0]
while q:
where, price, visited = q.popleft()
x, y = where
line, corner = price
if x == n-1 and y == n-1:
answer = min(answer, 100*line + 500*corner)
continue
if answer < 100*line + 500*corner:
continue
for k in range(4):
nx, ny = x + dx[k], y + dy[k]
if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 0 and not (nx, ny) in visited:
if x == 0 and y == 0:
q.append(((nx,ny),(line+1,corner), visited+[(nx, ny)]))
else:
# 직선
if (visited[-2][0]==nx==visited[-1][0]) or (visited[-1][1]==ny==visited[-2][1]):
q.append(((nx,ny), (line+1,corner),visited+[(nx, ny)]))
else:
q.append(((nx,ny), (line+1,corner+1),visited+[(nx, ny)]))
return answer
풀이 2
dfs를 이용한 풀이. 풀이 1보다는 개선되었지만 아직 부족하다. 테스트 케이스 10, 11, 12, 17, 18, 19에서 시간 초과
import sys
sys.setrecursionlimit(10**9)
answer = int(1e9)
def solution(board):
global answer
n = len(board)
dx = [0,1,0,-1]
dy = [1,0,-1,0]
visited = [[False]*n for _ in range(n)]
def dfs(x,y,px,py,cost):
global answer
if x == n-1 and y == n-1:
answer = min(answer, cost)
return
if answer <= cost:
return
for k in range(4):
nx, ny = x + dx[k], y + dy[k]
if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 0 and visited[nx][ny] == False:
visited[nx][ny] = True
if x == 0 and y == 0:
dfs(nx, ny, x, y, cost+100)
else:
# 직선
if (px == x == nx) or (py == y == ny):
dfs(nx, ny, x, y, cost+100)
else:
dfs(nx, ny, x, y, cost+600)
visited[nx][ny] = False
visited[0][0] = True
dfs(0, 0, 0, 0 ,0)
return answer
풀이 3
bfs + dp를 섞어서 푸니 풀렸다. nprice 배열을 통해 저장해주면서 가지치기를 늘렸다.
from collections import deque
def solution(board):
answer = int(1e9)
n = len(board)
q = deque()
q.append(((0,0),0, [(0,0)]))
dx = [0,1,0,-1]
dy = [1,0,-1,0]
nprice = [[int(1e9)]*n for _ in range(n)]
nprice[0][0] = 0
while q:
where, price, visited = q.popleft()
x, y = where
if price > nprice[x][y]:
continue
else:
nprice[x][y] = price
if x == n-1 and y == n-1:
answer = min(answer, price)
continue
if answer <= price:
continue
for k in range(4):
nx, ny = x + dx[k], y + dy[k]
if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 0 and not (nx, ny) in visited:
if x == 0 and y == 0:
q.append(((nx,ny), price+100, visited+[(nx, ny)]))
else:
# 직선
if (visited[-2][0]==nx==visited[-1][0]) or (visited[-1][1]==ny==visited[-2][1]):
q.append(((nx,ny), price+100,visited+[(nx, ny)]))
else:
q.append(((nx,ny),price+600,visited+[(nx, ny)]))
return answer
풀이 4
풀이 3과 비슷한 방법으로 이번엔 dfs에 적용시켰다.
import sys
sys.setrecursionlimit(10**9)
answer = int(1e9)
def solution(board):
global answer
n = len(board)
dx = [0,1,0,-1]
dy = [1,0,-1,0]
visited = [[False]*n for _ in range(n)]
nprice = [[int(1e9)]*n for _ in range(n)]
nprice[0][0] = 0
def dfs(x,y,px,py,cost):
global answer
if cost > nprice[x][y]:
return
else:
nprice[x][y] = cost
if x == n-1 and y == n-1:
answer = min(answer, cost)
return
if answer <= cost:
return
for k in range(4):
nx, ny = x + dx[k], y + dy[k]
if 0 <= nx < n and 0 <= ny < n and board[nx][ny] == 0 and visited[nx][ny] == False:
visited[nx][ny] = True
if x == 0 and y == 0:
dfs(nx, ny, x, y, cost+100)
else:
# 직선
if (px == x == nx) or (py == y == ny):
dfs(nx, ny, x, y, cost+100)
else:
dfs(nx, ny, x, y, cost+600)
visited[nx][ny] = False
visited[0][0] = True
dfs(0, 0, 0, 0 ,0)
return answer
Leave a comment