알고리즘 문제 풀이

137. [삼성기출33]Python 백준 19238 번 스타트 택시 자세한 설명 및 반례

코딩하는 덕구 🐶 2023. 8. 8. 13:06
728x90

안녕하세요 코딩하는 덕구입니다.

파이썬 백준 19238 스타트 택시입니다.

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

 

19238번: 스타트 택시

첫 줄에 N, M, 그리고 초기 연료의 양이 주어진다. (2 ≤ N ≤ 20, 1 ≤ M ≤ N2, 1 ≤ 초기 연료 ≤ 500,000) 연료는 무한히 많이 담을 수 있기 때문에, 초기 연료의 양을 넘어서 충전될 수도 있다. 다

www.acmicpc.net

백준 19238 스타트 택시 파이썬 코드입니다. 설명은 아래에 있습니다.

from collections import deque
N, M, fuel = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(N)]
x, y = map(int, input().split())
x, y = x-1, y-1
info = [list(map(int, input().split())) for _ in range(M)]
#승객 2 도착 3
index_p = {}
cord_d = {}

for num, i in enumerate(info):
    x1, y1, x2, y2 = i
    board[x1 - 1][y1 - 1] = 2
    index_p[(x1 - 1, y1 - 1)] = num
    cord_d[num] = [x2 - 1, y2 - 1]

dx = [-1, 0, 0, 1]
dy = [0, -1, 1, 0]

def solve():
    global x, y, fuel

    while M > 0:
        x, y, fuel, destx, desty = find_passenger(x, y, fuel)
        if fuel < 0:
            return -1

        fuel = go_destination(x, y, fuel, destx, desty)
        x, y = destx, desty
        if fuel < 0:
            return -1
    return fuel

def find_passenger(x, y, fuel):
    global M
    distance = 1e9
    candidate = [] #승객 후보
    queue = deque()
    queue.append([x, y, fuel])
    visited = [[False for _ in range(N)] for _ in range(N)]
    visited[x][y] = True

    while queue:
        x, y, fuel = queue.popleft()
        start = fuel
        if board[x][y] == 2: #도착한 경우
            if fuel >= 0:
                if start - fuel <= distance:
                    candidate.append([x, y, fuel])
                    distance = start - fuel

        for i in range(4):
            nx, ny = dx[i] + x, dy[i] + y
            if 0 <= nx < N and 0 <= ny < N:
                if not visited[nx][ny] and board[nx][ny] != 1 and 0 < fuel:
                    visited[nx][ny] = True
                    queue.append([nx, ny, fuel - 1])

    if candidate:
        candidate.sort(key=lambda x : (-x[2], x[0], x[1]))
        x, y, fuel= candidate[0]
        board[x][y] = 0
        M -= 1
        num = index_p[(x, y)]
        destx, desty = cord_d[num]
        return [x, y, fuel, destx, desty]

    return [-1, -1, -1, -1, -1] #갈 곳이 없는 경우

def go_destination(x, y, fuel, destx, desty):
    queue = deque()
    origin = fuel
    queue.append([x, y, fuel])
    visited = [[False for _ in range(N)] for _ in range(N)]
    visited[x][y] = True

    while queue:
        x, y, fuel = queue.popleft()
        if [x, y] == [destx, desty]:
            if fuel < 0:
                return -1
            return fuel + (origin - fuel) * 2

        for i in range(4):
            nx, ny = dx[i] + x, dy[i] + y
            if 0 <= nx < N and 0 <= ny < N:
                if not visited[nx][ny] and board[nx][ny] != 1 and fuel > 0:
                    visited[nx][ny] = True
                    queue.append([nx, ny, fuel - 1])

    return -1

print(solve())

 

문제 접근

1. 가장 가까운 승객을 찾기위한 BFS 1번

2. 최단 거리로 도착에 가기위한 BFS 1번

3. 위의 BFS들을 연료가 떨어지거나 모든 승객을 태울 때 까지 반복합니다.

4. 최단거리의 승객이 여러명인 경우 열과 행이 가장 작은 숫자인 승객을 태웁니다.

이때 BFS탐색을 [상, 좌, 우, 하]순으로 시도하고, 가장 먼저 만난 승객을 태우게 되면

4번의 조건을 통과하지 못합니다.

[좌, 좌, 좌],  [우, 우, 상] 의 경우 1 번째 경우가 먼저 탐색이 되지만

우선순위에서 2 번째 경우가 앞서는 반례가 있습니다.

 

Python 코드 구현

그래프의 상태, 승객과, 도착지, 방향을 구현하기 위한 코드들을 작성합니다.

index_p의 경우 좌표를 넣으면 몇번 승객인지 알려주는 dictionary 입니다.

cord_d는 번호를 넣으면 도착 좌표를 알려줍니다.

from collections import deque
N, M, fuel = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(N)]
x, y = map(int, input().split())
x, y = x-1, y-1
info = [list(map(int, input().split())) for _ in range(M)]
#승객 2 도착 3
index_p = {}
cord_d = {}

for num, i in enumerate(info):
    x1, y1, x2, y2 = i
    board[x1 - 1][y1 - 1] = 2
    index_p[(x1 - 1, y1 - 1)] = num
    cord_d[num] = [x2 - 1, y2 - 1]

dx = [-1, 0, 0, 1]
dy = [0, -1, 1, 0]

 

1. find_passenger 를 통해 조건에 맞는 최단거리 승객을 찾습니다.

2. go_destination을 통해 최단거리로 도착지로 이동한 후 연료를 채웁니다.

3. 모든 승객을 다 태울때 까지 반복합니다.

4. 이때 연료가 다 떨어지거나 승객 혹은 도착지를 찾을 수 없으면 -1 을 return 합니다.

def solve():
    global x, y, fuel

    while M > 0:
        x, y, fuel, destx, desty = find_passenger(x, y, fuel)
        if fuel < 0:
            return -1

        fuel = go_destination(x, y, fuel, destx, desty)
        x, y = destx, desty
        if fuel < 0:
            return -1
    return fuel
print(solve())

 

조건에 맞는 최단거리의 승객을 찾는 함수 find_passenger입니다.

candidate는 승객 후보를 담는 list입니다.

승객을 찾게되면 기존보다 distance가 크지 않은지 확인 한 후, distance를 업데이트 합니다.

조건에 맞다면 cansdidate에 추가합니다.

def find_passenger(x, y, fuel):
    global M
    distance = 1e9
    candidate = [] #승객 후보
    queue = deque()
    queue.append([x, y, fuel])
    visited = [[False for _ in range(N)] for _ in range(N)]
    visited[x][y] = True

    while queue:
        x, y, fuel = queue.popleft()
        start = fuel
        if board[x][y] == 2: #도착한 경우
            if fuel >= 0:
                if start - fuel <= distance:
                    candidate.append([x, y, fuel])
                    distance = start - fuel

 

BFS 탐색은 이렇게 진행해주시고

for i in range(4):
    nx, ny = dx[i] + x, dy[i] + y
    if 0 <= nx < N and 0 <= ny < N:
        if not visited[nx][ny] and board[nx][ny] != 1 and 0 < fuel:
            visited[nx][ny] = True
            queue.append([nx, ny, fuel - 1])

 

탐색이 끝나면 후보가 있는지 확인 한 후

연료가 가장 많이 남고, 좌표가 작은 순으로 후보를 정렬 합니다.

이후 택시 좌표, 남은 연료, 도착 좌표를 return 합니다.

if candidate:
    candidate.sort(key=lambda x : (-x[2], x[0], x[1]))
    x, y, fuel= candidate[0]
    board[x][y] = 0
    M -= 1
    num = index_p[(x, y)]
    destx, desty = cord_d[num]
    return [x, y, fuel, destx, desty]

return [-1, -1, -1, -1, -1] #갈 곳이 없는 경우

 

다음은 도착지점으로 가는 함수 go_destination입니다. 

사용한 연료를 * 2를 해주어 남은 연료와 더해 연료를 업데이트 합니다.

def go_destination(x, y, fuel, destx, desty):
    queue = deque()
    origin = fuel
    queue.append([x, y, fuel])
    visited = [[False for _ in range(N)] for _ in range(N)]
    visited[x][y] = True

    while queue:
        x, y, fuel = queue.popleft()
        if [x, y] == [destx, desty]:
            if fuel < 0:
                return -1
            return fuel + (origin - fuel) * 2

        for i in range(4):
            nx, ny = dx[i] + x, dy[i] + y
            if 0 <= nx < N and 0 <= ny < N:
                if not visited[nx][ny] and board[nx][ny] != 1 and fuel > 0:
                    visited[nx][ny] = True
                    queue.append([nx, ny, fuel - 1])

    return -1

 

백준 19238 스타트 택시 Python 코드였습니다.

감사합니다.

728x90