3월 3주차 코딩테스트


목차

  1. 한밤의 습격자
  2. 2회 윷놀이 최강자전
  3. DP같은 PQ
  4. 나누어 떨어뜨리기
  5. 수영장

1. 한밤의 습격자

풀이

from collections import deque

"""
 * BFS 문제의 변형입니다.
 * 일반 BFS 미로찾기에, 낮/밤에는 지도가 달라지는 기믹을 적용합니다.
 * visited 외에 waited를 하나 더 만들어서, 가만히 기다리는 턴 수를 기록합니다.
 * 가만히 기다리는 턴 수는 최대 9턴(낮+밤이 한바퀴 도는 동안)까지만 기다려보면 되기 때문입니다.
 * 새로운 곳에 도달하면, 상하좌우 이동하는 것 외에 1~9턴 대기하고 출발하는 것도 같이 시도해 보는 것이지요!
 * 또한 지도를 하나 더 만들어, 밤에는 밤 지도를 참고합니다.
"""

from copy import deepcopy


def solution(maze):
    N = len(maze)
    M = len(maze[0])

    q = []
    q.append((0, 0, 0))

    di = [0, 0, 1, -1, 0]
    dj = [1, -1, 0, 0, 0]

    maze_night = deepcopy(maze)
    for i in range(N):
        for j in range(M):
            if maze[i][j] == 2:
                maze_night[i][j] = 3
            elif i > 0 and maze[i-1][j] == 2:
                maze_night[i][j] = 3
            elif i < N-1 and maze[i+1][j] == 2:
                maze_night[i][j] = 3
            elif j > 0 and maze[i][j-1] == 2:
                maze_night[i][j] = 3
            elif j < M-1 and maze[i][j+1] == 2:
                maze_night[i][j] = 3

    mazes = [maze, maze_night]
    visited = [[0 for _ in range(M)] for _ in range(N)]
    waited = [[0 for _ in range(M)] for _ in range(N)]

    while q:
        i, j, t = q.pop(0)
        d = (t // 5) % 2 == 1

        if mazes[d][i][j] == 3:
            continue

        if (i, j) == (N-1, M-1):
            return visited[i][j]

        for di_, dj_ in zip(di, dj):
            new_i = i + di_
            new_j = j + dj_

            if (new_i, new_j) == (i, j):
                if waited[new_i][new_j] >= 10:
                    continue
                q.append((new_i, new_j, t + 1))
                waited[new_i][new_j] += 1
                visited[new_i][new_j] += 1

            if new_i < 0 or new_i >= N or new_j < 0 or new_j >= M:
                continue

            if visited[new_i][new_j] > 0:
                continue

            next_d = ((t+1) // 5) % 2 == 1
            if mazes[next_d][new_i][new_j] == 0 or mazes[next_d][new_i][new_j] == 2:
                q.append((new_i, new_j, t + 1))
                visited[new_i][new_j] = visited[i][j] + 1

    return -1

Testcase

# 테스트 1
입력값 〉 [[0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0], [0, 1, 0, 2, 0, 0], [1, 1, 0, 1, 0, 1], [0, 0, 0, 0, 1, 0], [1, 1, 1, 0, 2, 0]]
기댓값 〉 22

2. 2회 윷놀이 최강자전

풀이

"""
 * 이번 윷놀이 문제는 DP 문제입니다.
 * 따져줘야 하는 경우의 수가 매우 많기 때문에, 이것을 BFS로 하기에는 적절하지 않습니다.
 * 따져줘야 하는 종류는 아래와 같습니다.
 * 
 * 1. 시작점에서 출발하여 도개걸
 * 2. 숏컷을 타고 도개걸
 * 3. 시작점에서 출발하여 윷모 + 도개걸윷모
 * 4. 숏컷을 타고 윷모 + 도개걸윷모
 * 5. 시작점에서 출발할아여 윷모 + 숏컷을 타고 도개걸윷모
 * 6. 숏컷을 타고 윷모 + 숏컷을 타고 도개걸윷모
 * 
 * 아주 다양한 경우의 수가 있고, 코드에서 복잡한 숫자들과 for, if문은 모두 위를 커버하기 위함입니다.
"""

def solution(N, edges):
    dp = [float('inf') for _ in range(N+1)]
    dp[0] = 0

    first_moves = [0, 4, 5]
    second_moves1 = [1, 2, 3]
    second_moves2 = [1, 2, 3, 4, 5]
    for i in range(N):
        shortcuts = list(map(lambda e: e[1] - 1,
         filter(lambda e: e[0] == i, edges)))
        for start in [i] + shortcuts:
            for move1 in first_moves:
                j = start + move1
                if move1 > 0:
                    shortcuts2 = list(map(lambda e: e[1] - 1, 
                        filter(lambda e: e[0] == j, edges)))
                else:
                    shortcuts2 = []
                for start2 in [j] + shortcuts2:
                    second_moves = second_moves1 if move1 == 0 else second_moves2
                    for move2 in second_moves:
                        k = start2 + move2
                        if k <= N:
                            dp[k] = min(dp[k], dp[i] + 1)
    return dp[N]

Testcase

# 테스트 1
입력값 〉 34, [[1, 4], [6, 12], [15, 24]]
기댓값 〉 3

# 테스트 2
입력값 〉 100, [[12, 40], [18, 53], [59, 89]]
기댓값 〉 5

3. DP같은 PQ

풀이

from heapq import heappop, heappush

"""
 * DP문제처럼 보이지만, 우선순위 큐 문제입니다.
 * DP로 풀려고 하면 O(N*k) 연산을 해야 하므로, 너무 느립니다.
 * 우선순위 큐로 따질 필요가 없는 경우는 버려서 빠르게 연산합니다.
"""
def solution(arr, k):
    heap = []

    heappush(heap, (-arr[0], 0))

    for i in range(1, len(arr)):
        while heap and heap[0][1] < i-k:
            heappop(heap)
        heappush(heap, (heap[0][0] - arr[i], i))

    while heap[0][1] != len(arr) - 1:
        heappop(heap)

    return -heappop(heap)[0]

Testcase

# 테스트 1
입력값 〉 [1, -1, -20, 4, -7, 5], 2
기댓값 〉 9

# 테스트 2
입력값 〉 [3, -4, 5, 1, 3, -5, -12, 4, -4, 5], 3
기댓값 〉 21

4. 나누어 떨어뜨리기

풀이

"""
 * 수학적인 센스가 필요한 문제입니다.
 * numsDivided의 숫자를 모두 나누어 떨어지게 하려면,
 * numsDivided의 최대공약수를 나누어 떨어지게 하면 됩니다.
 * 이것을 이용해서 나눗셈 연산을 최소로 하는 것이 핵심!
"""
from math import gcd

def solution(numsDivide, numsDivided):
    arr = sorted(list(set(numsDivide)))
    div = gcd(*list(set(numsDivided)))
    
    for n in arr:
        if div % n == 0:
            return len(list(filter(lambda x: x < n, numsDivide)))
            
    return -1

Testcase

# 테스트 1
입력값 〉 [2, 9, 3, 6, 2, 4, 3], [9, 18, 27, 9, 15]
기댓값 〉 2

5. 수영장

풀이

from heapq import heappush, heappop


def solution(heights):
    heap = []
    
    n = len(heights)
    m = len(heights[0])
    
    visited = [[0 for _ in range(m)] for _ in range(n)]
    
    for i in range(n):
        for j in range(m):
            if i == 0 or j == 0 or i == n-1 or j == m-1:
                heappush(heap, (heights[i][j], (i, j)))
                visited[i][j] = 1
    
    directions = [(-1, 0), (1, 0), (0, 1), (0, -1)]
    
    water = 0
    while heap:
        h, pos = heappop(heap)
        i, j = pos
        
        for direction in directions:
            new_i = i + direction[0]
            new_j = j + direction[1]
            
            if (new_i < 0 or new_i > n-1 or new_j < 0 or new_j > m-1):
                continue
            
            if visited[new_i][new_j] > 0:
                continue
            
            visited[new_i][new_j] = 1
            
            if h > heights[new_i][new_j]:
                water += h - heights[new_i][new_j]
                heights[new_i][new_j] = h
            
            heappush(heap, (heights[new_i][new_j], (new_i, new_j)))
    
    return water

Testcase

# 테스트 1
입력값 〉 [[3, 4, 5, 4, 3, 3], [3, 2, 1, 1, 2, 3], [4, 2, 1, 1, 2, 3], [3, 3, 3, 3, 5, 3]]
기댓값 〉 12

# 테스트 2
입력값 〉 [[3, 3, 3, 3, 3, 3], [3, 1, 3, 1, 1, 3], [3, 1, 3, 2, 1, 3], [3, 3, 3, 3, 3, 3]]
기댓값 〉 11

출처

해당 문제와 코드는 제로베이스(ZeroBase)에서 제공받았습니다.

모든 자료는 저작권법에 의하여 보호받는 저작물로서 이에 대한 무단 복제 및 배포를 원칙적으로 금합니다.