백준 온라인 저지, 구현 / 19237번: 어른상어 (파이썬 / , 백준 골드문제, 삼성 SW 기출문제)

2021. 12. 6. 23:30알고리즘/구현

728x90
반응형

문제

청소년 상어는 더욱 자라 어른 상어가 되었다. 상어가 사는 공간에 더 이상 물고기는 오지 않고 다른 상어들만이 남아있다. 상어에는 1 이상 M 이하의 자연수 번호가 붙어 있고, 모든 번호는 서로 다르다. 상어들은 영역을 사수하기 위해 다른 상어들을 쫓아내려고 하는데, 1의 번호를 가진 어른 상어는 가장 강력해서 나머지 모두를 쫓아낼 수 있다.

N×N 크기의 격자 중 M개의 칸에 상어가 한 마리씩 들어 있다. 맨 처음에는 모든 상어가 자신의 위치에 자신의 냄새를 뿌린다. 그 후 1초마다 모든 상어가 동시에 상하좌우로 인접한 칸 중 하나로 이동하고, 자신의 냄새를 그 칸에 뿌린다. 냄새는 상어가 k번 이동하고 나면 사라진다.

각 상어가 이동 방향을 결정할 때는, 먼저 인접한 칸 중 아무 냄새가 없는 칸의 방향으로 잡는다. 그런 칸이 없으면 자신의 냄새가 있는 칸의 방향으로 잡는다. 이때 가능한 칸이 여러 개일 수 있는데, 그 경우에는 특정한 우선순위를 따른다. 우선순위는 상어마다 다를 수 있고, 같은 상어라도 현재 상어가 보고 있는 방향에 따라 또 다를 수 있다. 상어가 맨 처음에 보고 있는 방향은 입력으로 주어지고, 그 후에는 방금 이동한 방향이 보고 있는 방향이 된다.

모든 상어가 이동한 후 한 칸에 여러 마리의 상어가 남아 있으면, 가장 작은 번호를 가진 상어를 제외하고 모두 격자 밖으로 쫓겨난다.

<그림 1>

우선 순위
상어 1 상어 2 상어 3 상어 4
↓ ← ↑ → ↓ → ← ↑ → ← ↓ ↑ ← → ↑ ↓
→ ↑ ↓ ← ↓ ↑ ← → ↑ → ← ↓ ← ↓ → ↑
← → ↓ ↑ ← → ↑ ↓ ↑ ← ↓ → ↑ → ↓ ←
→ ← ↑ ↓ → ↑ ↓ ← ← ↓ ↑ → ↑ → ↓ ←

<표 1>

<그림 1>은 맨 처음에 모든 상어가 자신의 냄새를 뿌린 상태를 나타내며, <표 1>에는 각 상어 및 현재 방향에 따른 우선순위가 표시되어 있다. 이 예제에서는 k = 4이다. 왼쪽 하단에 적힌 정수는 냄새를 의미하고, 그 값은 사라지기까지 남은 시간이다. 좌측 상단에 적힌 정수는 상어의 번호 또는 냄새를 뿌린 상어의 번호를 의미한다.

<그림 2>

<그림 3>

<그림 2>는 모든 상어가 한 칸 이동하고 자신의 냄새를 뿌린 상태이고, <그림 3>은 <그림 2>의 상태에서 한 칸 더 이동한 것이다. (2, 4)에는 상어 2과 4가 같이 도달했기 때문에, 상어 4는 격자 밖으로 쫓겨났다.

<그림 4>

<그림 5>

<그림 4>은 격자에 남아있는 모든 상어가 한 칸 이동하고 자신의 냄새를 뿌린 상태, <그림 5>는 <그림 4>에서 한 칸 더 이동한 상태를 나타낸다. 상어 2는 인접한 칸 중에 아무 냄새도 없는 칸이 없으므로 자신의 냄새가 들어있는 (2, 4)으로 이동했다. 상어가 이동한 후에, 맨 처음에 각 상어가 뿌린 냄새는 사라졌다.

이 과정을 반복할 때, 1번 상어만 격자에 남게 되기까지 몇 초가 걸리는지를 구하는 프로그램을 작성하시오.

입력

첫 줄에는 N, M, k가 주어진다. (2 ≤ N ≤ 20, 2 ≤ M ≤ N2, 1 ≤ k ≤ 1,000)

그 다음 줄부터 N개의 줄에 걸쳐 격자의 모습이 주어진다. 0은 빈칸이고, 0이 아닌 수 x는 x번 상어가 들어있는 칸을 의미한다.

그 다음 줄에는 각 상어의 방향이 차례대로 주어진다. 1, 2, 3, 4는 각각 위, 아래, 왼쪽, 오른쪽을 의미한다.

그 다음 줄부터 각 상어의 방향 우선순위가 상어 당 4줄씩 차례대로 주어진다. 각 줄은 4개의 수로 이루어져 있다. 하나의 상어를 나타내는 네 줄 중 첫 번째 줄은 해당 상어가 위를 향할 때의 방향 우선순위, 두 번째 줄은 아래를 향할 때의 우선순위, 세 번째 줄은 왼쪽을 향할 때의 우선순위, 네 번째 줄은 오른쪽을 향할 때의 우선순위이다. 각 우선순위에는 1부터 4까지의 자연수가 한 번씩 나타난다. 가장 먼저 나오는 방향이 최우선이다. 예를 들어, 우선순위가 1 3 2 4라면, 방향의 순서는 위, 왼쪽, 아래, 오른쪽이다.

맨 처음에는 각 상어마다 인접한 빈 칸이 존재한다. 따라서 처음부터 이동을 못 하는 경우는 없다.

출력

1번 상어만 격자에 남게 되기까지 걸리는 시간을 출력한다. 단, 1,000초가 넘어도 다른 상어가 격자에 남아 있으면 -1을 출력한다.

예제 입력 1

5 4 4
0 0 0 0 3
0 2 0 0 0
1 0 0 0 4
0 0 0 0 0
0 0 0 0 0
4 4 3 1
2 3 1 4
4 1 2 3
3 4 2 1
4 3 1 2
2 4 3 1
2 1 3 4
3 4 1 2
4 1 2 3
4 3 2 1
1 4 3 2
1 3 2 4
3 2 1 4
3 4 1 2
3 2 4 1
1 4 2 3
1 4 2 3

예제 출력 1

14

문제에 나온 그림과 같다.

예제 입력 2

4 2 6
1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 2
4 3
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3

예제 출력 2

26

예제 입력 3

5 4 1
0 0 0 0 3
0 2 0 0 0
1 0 0 0 4
0 0 0 0 0
0 0 0 0 0
4 4 3 1
2 3 1 4
4 1 2 3
3 4 2 1
4 3 1 2
2 4 3 1
2 1 3 4
3 4 1 2
4 1 2 3
4 3 2 1
1 4 3 2
1 3 2 4
3 2 1 4
3 4 1 2
3 2 4 1
1 4 2 3
1 4 2 3

예제 출력 3

-1

예제 입력 4

5 4 10
0 0 0 0 3
0 0 0 0 0
1 2 0 0 0
0 0 0 0 4
0 0 0 0 0
4 4 3 1
2 3 1 4
4 1 2 3
3 4 2 1
4 3 1 2
2 4 3 1
2 1 3 4
3 4 1 2
4 1 2 3
4 3 2 1
1 4 3 2
1 3 2 4
3 2 1 4
3 4 1 2
3 2 4 1
1 4 2 3
1 4 2 3

예제 출력 4

-1

접근 방법

- 0. 격자 정보를 이중 리스트(board)로, 상어번호를 인덱스로 하는 방향 정보(now_direction)를 리스트로, 상어 번호를 인덱스로 하는 리스트에서 현재 방향에 따른 우선순위를 인덱스로 하는 삼중 리스트(direction)를 입력받는다.
- 1. 상어를 이동시킨 후엔 이동하기 전의 위치에 냄새를 남긴다. 단, 상어가 죽은 경우에도 냄새를 남긴다.
- 2. 모든 격자를 탐색하며 상어가 있는 경우에는 해당 상어를 현재 있는 상어 리스트에 삽입한 뒤, 우선순위에 따라 이동시킨다.
- 2-1. 반복문을 통해 0부터 3까지 우선순위를 정해 direction에 입력 후, 해당 방향이 0일 경우 이를 멈추고 그 위치로 이동한다.
- 2-2. 만약 우선순위를 통해 탐색한 위치가 0이 아니지만 상어가 있다면 그 상어와의 번호를 비교해 더 낮은 번호의 상어만 남긴다.
- 2-3. 만약 모든 위치가 0이 아니고, 냄새가 있는 위치라면 상하좌우의 위치 중 본인의 냄새가 나는 위치로 다시 이동한다.
- 3. 상어의 이동이 끝난 뒤, 모든 격자를 탐색하며 냄새가 있는 정보의 k를 1씩 줄인다. 이때 k가 0이되면 해당 격자는 0으로 값을 바꾼다.(반복문의 특성을 이용해 순서대로 진행하지 않고, 1번과 3번을 함께 2번보다 선행되도록 함)
- 4. 1 ~ 3번이 끝나고 시간을 1초 증가시킨 뒤, 1 ~ 3번을 반복한다.
- 5. 1번 상어만 남게되면 모든 반복을 멈추고 몇 초가 지났는 지 출력한다.
- 6. 만약 1000초가 지났다면 반복을 멈추고 -1을 출력한다.

코드

# https://www.acmicpc.net/problem/19237
# 접근방법
# 0. 격자 정보를 이중 리스트(board)로, 상어번호를 인덱스로 하는 방향 정보(now_direction)를 리스트로, 상어 번호를 인덱스로 하는 리스트에서 현재 방향에 따른 우선순위를 인덱스로 하는 삼중 리스트(direction)를 입력받는다.
# 1. 상어를 이동시킨 후엔 이동하기 전의 위치에 냄새를 남긴다. 단, 상어가 죽은 경우에도 냄새를 남긴다.
# 2. 모든 격자를 탐색하며 상어가 있는 경우에는 해당 상어를 현재 있는 상어 리스트에 삽입한 뒤, 우선순위에 따라 이동시킨다. 
# 2-1. 반복문을 통해 0부터 3까지 우선순위를 정해 direction에 입력 후, 해당 방향이 0일 경우 이를 멈추고 그 위치로 이동한다.
# 2-2. 만약 우선순위를 통해 탐색한 위치가 0이 아니지만 상어가 있다면 그 상어와의 번호를 비교해 더 낮은 번호의 상어만 남긴다.
# 2-3. 만약 모든 위치가 0이 아니고, 냄새가 있는 위치라면 상하좌우의 위치 중 본인의 냄새가 나는 위치로 다시 이동한다.
# 3. 상어의 이동이 끝난 뒤, 모든 격자를 탐색하며 냄새가 있는 정보의 k를 1씩 줄인다. 이때 k가 0이되면 해당 격자는 0으로 값을 바꾼다.(반복문의 특성을 이용해 순서대로 진행하지 않고, 1번과 3번을 함께 2번보다 선행되도록 함)
# 4. 1 ~ 3번이 끝나고 시간을 1초 증가시킨 뒤, 1 ~ 3번을 반복한다.
# 5. 1번 상어만 남게되면 모든 반복을 멈추고 몇 초가 지났는 지 출력한다.
# 6. 만약 1000초가 지났다면 반복을 멈추고 -1을 출력한다.

import sys
input = sys.stdin.readline
n, m, k = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(n)]
now_direction = [0] # now_direction[상어번호]
direction = [[[] for _ in range(5)] for _ in range(m+1)] # direction[상어번호][현재방향(now_direction)][우선순위(0 ~ 3)]
for x in list(map(int, input().split())):
    now_direction.append(x)
    
for i in range(1, m+1):
    for j in range(1, 5):
        for x in list(map(int, input().split())):
            direction[i][j].append(x)

step = [[], [-1, 0], [1, 0], [0, -1], [0, 1]] # step[방향(direction)] -> 상하좌우에 대한 dx, dy


def move(r, c):
    shark = board[r][c][0]
    last_place = []
    # 2-1. 반복문을 통해 0부터 3까지 우선순위를 정해 direction에 입력 후, 해당 방향이 0일 경우 이를 멈추고 그 위치로 이동한다.
    for priority in range(4):
        dr, dc = step[direction[shark][now_direction[shark]][priority]]
        if 0<=r+dr<=n-1 and 0<=c+dc<=n-1:
            # 2-2. 만약 우선순위를 통해 탐색한 위치가 0이 아니지만 상어가 있다면 그 상어와의 번호를 비교해 더 낮은 번호의 상어만 남긴다.
            if type(board[r+dr][c+dc]) == type(0):
                
                shark_list.append([shark, r+dr, c+dc])
                last_place = []
                d = direction[shark][now_direction[shark]][priority]
                break

            elif board[r+dr][c+dc][0] == shark and not last_place:
                last_place.append([shark, r+dr, c+dc])
                d = direction[shark][now_direction[shark]][priority]
    
    # 2-3. 만약 모든 위치가 0이 아니고, 냄새가 있는 위치라면 상하좌우의 위치 중 본인의 냄새가 나는 위치로 다시 이동한다.
    if last_place:
        shark_list.append(last_place[0])

    now_direction[shark] = d

def smell():
    for r in range(n):
        for c in range(n):
            # 1. 상어를 이동하기 전에 냄새를 남긴다. 단, 상어가 죽은 경우에도 냄새를 남긴다.
            if type(board[r][c]) == type(0) and board[r][c] > 0:
                board[r][c] = [board[r][c], k]

            # 3. 상어의 이동이 끝난 뒤, 모든 격자를 탐색하며 냄새가 있는 정보의 k를 1씩 줄인다. 이때 k가 0이되면 해당 격자는 0으로 값을 바꾼다.
            elif type(board[r][c]) == type([]):
                board[r][c][1] -= 1
                if board[r][c][1] == 0:
                    board[r][c] = 0


time = 0
shark_count = 0
while shark_count != 1:
    if time >= 1000:
        time = -1
        break
    
    smell()
    shark_count = 0
    shark_list = []
    for r in range(n):
        for c in range(n):
            if type(board[r][c]) == type([]) and board[r][c][1] == k:
                move(r, c)
    
    for shark, r, c in shark_list:
        if board[r][c] == 0 or type(board[r][c]) != type(0):
            board[r][c] = shark
            shark_count += 1
        else:
            board[r][c] = min(board[r][c], shark)
    
    time += 1

print(time)
728x90
반응형