목차

스타트 택시

ps
링크acmicpc.net/…
출처BOJ
문제 번호19238
문제명스타트 택시
레벨골드 4
분류

BFS

시간복잡도O(m*n^2)
인풋사이즈n<=20, m<=400
사용한 언어Python
제출기록32208KB / 296ms
최고기록172ms
해결날짜2020/12/16

풀이

코드

"""Solution code for "BOJ 19238. 스타트 택시".

- Problem link: https://www.acmicpc.net/problem/19238
- Solution link: http://www.teferi.net/ps/problems/boj/19238
"""

import collections
from typing import Callable, Generator, Iterable, List, Optional, Sequence, Tuple, TypeVar
T = TypeVar('T')
CellType = TypeVar('CellType')
PosType = Tuple[int, int]


INF = float('inf')


def bfs(start: T,
        get_next: Callable[[T], Iterable[T]]) -> Generator[T, None, None]:
    """Yields (node, distance) pairs for nodes in BFS-order."""
    yield (start, 0)
    visited = set()
    queue = collections.deque([(start, 0)])
    while queue:
        cur, dist = queue.popleft()
        for next_ in get_next(cur):
            if next_ not in visited:
                visited.add(next_)
                queue.append((next_, dist + 1))
                yield (next_, dist + 1)


def min_dist_targets(source: T,
                     get_next: Callable[[T], Iterable[T]],
                     is_target: Callable[[T], bool],
                     limit: Optional[int]) -> Tuple[List[T], Optional[int]]:
    targets = []
    for pos, dist in bfs(source, get_next):
        if limit and dist > limit:
            break
        if is_target(pos):
            targets.append(pos)
            limit = dist
    return (targets, limit)


def get_neighbors_from_grid(
    grid: Sequence[Sequence[CellType]],
    empty: CellType
) -> Callable[[PosType], Generator[PosType, None, None]]:
    """Returns a generator that yields neighbor cells in the grid."""
    row_count, col_count = len(grid), len(grid[0])

    def get_neighbors(pos):
        r, c = pos
        for dr, dc in ((-1, 0), (0, -1), (0, 1), (1, 0)):
            nr, nc = r + dr, c + dc
            if (0 <= nr < row_count
                and 0 <= nc < col_count
                    and grid[nr][nc] == empty):
                yield (nr, nc)
    return get_neighbors


def main():
    N, M, fuel = [int(x) for x in input().split()]
    grid = []
    for _ in range(N):
        grid.append([int(x) for x in input().split()])
    cur_row, cur_col = [int(x) for x in input().split()]
    passengers = {}
    for _ in range(M):
        r1, c1, r2, c2 = [int(x) for x in input().split()]
        passengers[(r1 - 1, c1 - 1)] = ((r1 - 1, c1 - 1), (r2 - 1, c2 - 1))

    get_next_fn = get_neighbors_from_grid(grid, empty=0)
    source = (cur_row - 1, cur_col - 1)
    for _ in range(M):
        targets, dist = min_dist_targets(
            source, get_next_fn, lambda pos: pos in passengers, fuel)
        if not targets:
            print(-1)
            break
        fuel -= dist
        pos = min(targets)
        source, sink = passengers[pos]
        del passengers[pos]

        targets, dist = min_dist_targets(
            source, get_next_fn, lambda pos: pos == sink, fuel)
        if not targets:
            print(-1)
            break
        fuel += dist
        source = sink
    else:
        print(fuel)


if __name__ == '__main__':
    main()