2022 현대 모비스 알고리즘 경진 대회 예선 문제 리뷰

Problem


Table of Contents


공통 사항

  • Python3 기준, 수행시간은 10초
  • 이에 따라, 계산 가능한 최대 횟수는 약 2억번

Dead or Arrive 설명

  • 각 차량은 v의 속도와 w의 내구도를 가짐
  • 속도가 다른 차량은 모두 결승선 통과
  • 속도가 같은 경우, 내구도가 가장 높은 차량만 결승선을 통과
  • 속도와 내구도가 모두 같은 경우, 차량 번호가 가장 큰 차량만 결승선 통과
  • 첫 번째 줄에 차량의 숫자 N이 입력
  • 두 번째 줄부터 N줄만큼 각 차량의 v, w가 주어진다.
  • N은 최대 2,000,000까지 주어지며, v는 최대 1,000, w는 최대 1,000,000,000까지 주어진다.
  • Mars Killaz의 차량 중 결승선에 들어온 차량의 번호의 합을 출력하시오.

Dead or Arrive 풀이

  • 일반적인 구현 문제로 보인다.
  • 우선 순위가 1차가 속도, 2차가 내구도, 3차가 차량 번호로 지정되도록 뽑으면 되는 문제
  • 처음에 한참, 같은 속도의 차량을 뽑아내서, 그 다음에 같은 내구도의 차량을 뽑은 다음… 이런 식으로 진행하려고 했더니, 정신아 아득해졌다..
  • 어찌됐든, 속도 중복이 없는 내구도가 가장 높거나 번호가 가장 높은 차량을 뽑는 것이므로, Dictionary를 이용하면 좋을 것 같았다.
  • 현재 입력된 속도가 Dictionary에 존재하는 지 확인한 후, 없다면, 단순 추가
  • 있다면, 기존 값과 비교를 통해 w가 높으면 교체, w도 같다면, 차량 숫자를 비교하여 높으면 교체와 같은 형식으로 진행하였다.
  • 코드는 다음과 같다.
# -*- coding: utf-8 -*-
# Dead or Arrive 문제
import sys
input = sys.stdin.readline

N = int(input())

vehicles = []

for i in range(1, N + 1):
    v, w = map(int, input().split())
    vehicles.append((v, w, i))
    
s_vehicles = sorted(vehicles, key=lambda x: x[0])

speed_dict = dict()

for v, w, i in s_vehicles:
    if not v in speed_dict:
        speed_dict[v] = (w, i)
    elif w > speed_dict[v][0]:
        speed_dict[v] = (w, i)
    elif w == speed_dict[v][0]:
        if i > speed_dict[v][1]:
            speed_dict[v] = (w, i)
    
result = 0        
for (w, i) in speed_dict.values():
     result += i
     
print(result)


주차 시스템 설명

  • 현대 백화점 주차장의 특별 주차 시스템
  • 주차장은 0, 1, 2로 구분
  • 0은 자동차가 없음
  • 1은 자동차가 있음
  • 2는 자동차는 없지만, 장애인 전용 주차 공간
  • 주차장이 주어지면, 1로 나누어진 곳을 기준으로, 여러 개의 구역이 나누어진다.
  • 나누어진 구역은 0인 칸의 개수만큼 +1점, 2인 칸의 개수만큼 -2점을 하여, 구역의 점수를 계산한다.
  • 여러 개의 구역 중 가장 높은 점수를 구하시오.
  • 첫 번째 줄에 주차장의 크기인 N, M이 주어짐.
  • 두 번째 줄부터 N줄에 걸쳐, M개의 숫자가 주어집니다. 각각의 값은 주차장의 상태를 나타냅니다.

주차 시스템 풀이

  • 결국 모든 주차 구역에 대해 BFS or DFS를 이용하여, 탐색하고, 탐색한 범위의 값들을 기준에 맞게 합산하여, 저장해두었다가, 모든 탐색이 끝난 후, 최대 값을 출력하면 된다.
  • BFS or DFS의 구현이 익숙하다면, 쉽게 해결할 수 있다.
  • 최대한 간단하게 코드를 작성을 했는데, 테스트 케이스 2개가 자꾸 해결이 안되었다.
  • 네트워크랑 PC 성능의 문제가 영향이 없다고는 하는데, 간혹 있는 경우가 있어서 다시 진행해볼 예정이다.
  • 코드는 다음과 같다.
# -*- coding: utf-8 -*-
import sys
from collections import deque
input = sys.stdin.readline

N, M = map(int, input().split())

parking = [list(map(int, input().split())) for _ in range(N)]

dxy = [[1, 0], [0, 1], [-1, 0], [0, -1]]

area_score = 0

for i in range(N):
    for j in range(M):
        if parking[i][j] == 1:
            continue

        Q = deque([(i, j)])
        if parking[i][j] == 0:
            tmp_score = 1
        else:
            tmp_score = -2
        parking[i][j] = 1

        while Q:
            now_x, now_y = Q.popleft()
            for dx, dy in dxy:
                tmp_x = now_x + dx
                tmp_y = now_y + dy
                if not (0 <= tmp_x < N and 0 <= tmp_y < M):
                    continue
                if parking[tmp_x][tmp_y] != 1:
                    Q.append((tmp_x, tmp_y))
                    if parking[tmp_x][tmp_y] == 0:
                        tmp_score += 1
                    else:
                        tmp_score += -2
                    parking[tmp_x][tmp_y] = 1

        if area_score < tmp_score:
            area_score = tmp_score

print(area_score)


ADAS 시스템 설명

  • 2차원 좌표평면으로 가상 주행 환경을 구성
  • $0 <= a < W$, $0 <= b < H$인 (a, b) 좌표의 점은 일반 점, P- 점, 점 S, 점 E로 표현
  • 가상 주행 시스템은 점 S에서 시작하여 점 E로 이동하며, 점 E에 도착하면 주행이 종료
  • 주행 과정에서 ADAS 시스템은 다음 조건을 따라 이동
    • (a, b)에서 상하좌우 4개의 방향 중, 방문한 적 없는 점을 이동 후보 목록에 추가 (좌표 평면 바깥이면 제외)
    • 다음 방문할 점은 아래 우선 순위에 따라서 결정
      • 점 E는 우선 순위가 가장 높음
      • P- 점은 그 다음으로 높음
      • 일반점이 가장 낮음
      • 우선 순위가 동일한 점이 여러 개 있다면, a가 작은 것이 우선, a도 같다면 b가 작은 것이 우선 순위가 높다.
  • 주행 중 위험 점수는 0점에서 시작하여, 다음 규칙에 따라 위험 점수가 증가
    • 점 S점 E는 위험 점수 증가 없음
    • 일반 점은 해당 점을 중심으로 $3 \times 3$ 크기 안에서 자신을 제외한 P- 점의 개수만큼 위험 점수가 증가
    • P- 점일반 점과 동일한 방법으로 측정한 후, -3점 만큼 위험 점수가 증가
  • ADAS 시스템이 목적지에 도착하면 종료하며, 위험 점수를 출력
  • WH는 최대 1,000까지 주어진다.
  • 첫째 줄에 W, H가 주어지며, 둘째 줄부터 W줄에 걸쳐 S, E, P, 0의 문자열이 주어진다.

ADAS 시스템 풀이

  • 우선 이동 후보에 넣은 후, 우선 순위에 따라 먼저 꺼내야하는 문제이다.
  • 이걸 우선 순위를 다 찾아서 꺼내면… $O(N)$이 될 것이다.
  • 우상단에서 우하단까지 이동한다면, 모든 점을 거쳐서 이동하며, 최대 $N^2$의 이동을 한다.
  • 최대 계산량은 $O(N^3)$이므로, 1,000,000,000번의 연산이 필요하다.
  • 10초의 시간으로 연산할 수 있는 양은 200,000,000번이므로, 어림도 없다.
  • 그렇다면, 우선 순위 큐를 쓴다면 어떨까?
  • 일반 큐의 탐색은 $O(N)$이지만, 우선 순위 큐의 탐색은 $O(logN)$이다.
  • 따라서 계산량이 $O(N^3)$에서 $O(N^2logN)$으로 바뀌며, 약 10,000,000번으로 해결이 되는 엄청난 성능을 볼 수 있다.
  • 그렇다면, 우선 순위에 맞춰서 잘 넣고, 잘 찾아주는 우선 순위 큐만 잘 구현한다면, 어렵지 않게 맞출 수 있을 것 같다.
  • 하지만, 이상하게도 5개의 테스트 케이스를 통과할 수 없었다. 왤까…. 아시면 댓글 부탁드립니다.
  • 코드는 다음과 같다.
# -*- coding: utf-8 -*-
import sys
import heapq

input = sys.stdin.readline

W, H = map(int, input().split())
virtual_env = [input().strip() for _ in range(W)]

def is_valid_coordinate(x, y):
    if 0 <= x < W and 0 <= y < H:
        return True
    return False

def find_S(virtual_env):
    for x, value in enumerate(virtual_env):
        y = value.find('S')
        if y == -1:
            continue
        return (x, y)

dx = [-1, 0, 1, 0]
dy = [0, -1, 0, 1]
hq = []
S_x, S_y = find_S(virtual_env)
heapq.heappush(hq, (0, S_x, S_y))
visited = set()
visited.add(S_x * H + S_y)

def calc_point(x, y):
    tmp_score = 0
    if virtual_env[x][y] == 'S' or virtual_env[x][y] == 'E':
        return 0
    if virtual_env[x][y] == 'P':
        tmp_score = -3
    for r in range(x - 1, x + 2):
        for c in range(y - 1, y + 2):
            if (r == x and c == y) or not is_valid_coordinate(r, c):
                continue
            if virtual_env[r][c] == 'P':
                tmp_score += 1
    return tmp_score
            

danger_point = 0
while hq:
    _, now_x, now_y = heapq.heappop(hq)
    if virtual_env[now_x][now_y] == 'E':
        break
    danger_point += calc_point(now_x, now_y)
    
    for direction in range(4):
        tmp_x = now_x + dx[direction]
        tmp_y = now_y + dy[direction]
        if (tmp_x * H + tmp_y) in visited:
            continue
        if not is_valid_coordinate(tmp_x, tmp_y):
            continue
        if virtual_env[tmp_x][tmp_y] == 'E':
            heapq.heappush(hq, (0, tmp_x, tmp_y))
        elif virtual_env[tmp_x][tmp_y] == 'P':
            heapq.heappush(hq, (1, tmp_x, tmp_y))
        elif virtual_env[tmp_x][tmp_y] == '0':
            heapq.heappush(hq, (2, tmp_x, tmp_y))
        visited.add(tmp_x * H + tmp_y)

print(danger_point)


Review

  • 역시 대회용 알고리즘도 기본적인 코딩테스트에 나오는 문제들을 포함하고는 있지만, 케이스가 아주 다양한 것 같다.
  • 게다가 처음 들어보는 KMP, MCMF 알고리즘이나, Trie 자료구조를 보고 리뷰는 멈추기로 했다.
  • 설명을 보니, 문자열을 빠르게 찾는 알고리즘이 KMP나, Trie 자료구조를 활용하는 방법이라고 한다.
  • MCMF 알고리즘은 최소 비용 최대 유량 알고리즘으로 최소 비용은 최단 거리 알고리즘인 Dijkstra또는 벨만포드 알고리즘을 사용하며, 각 상황에서의 최대 유량을 구해주면 된다고 하는데 잘 모르겠다.
  • 아직까지는 그저.. 코딩테스트를 푸는 정도의 수준으로만 가능할 것 같다.
  • 추후에 시간이 된다면, 종만북을 보고, 대회용 알고리즘도 리뷰할 수 있도록 하겠습니다… 나약한 저는 이만…

comments