Coding test(Python3)/Programmers

[level 3] 아이템 줍기

녜잉 2024. 7. 8. 17:51

1. 문제

다음과 같은 다각형 모양 지형에서 캐릭터가 아이템을 줍기 위해 이동하려 합니다.

 

지형은 각 변이 x축, y축과 평행한 직사각형이 겹쳐진 형태로 표현하며, 캐릭터는 이 다각형의 둘레(굵은 선)를 따라서 이동합니다.

만약 직사각형을 겹친 후 다음과 같이 중앙에 빈 공간이 생기는 경우, 다각형의 가장 바깥쪽 테두리가 캐릭터의 이동 경로가 됩니다.

 

단, 서로 다른 두 직사각형의 x축 좌표 또는 y축 좌표가 같은 경우는 없습니다.

 

즉, 위 그림처럼 서로 다른 두 직사각형이 꼭짓점에서 만나거나, 변이 겹치는 경우 등은 없습니다.

다음 그림과 같이 지형이 2개 이상으로 분리된 경우도 없습니다.

 

한 직사각형이 다른 직사각형 안에 완전히 포함되는 경우 또한 없습니다.

 

지형을 나타내는 직사각형이 담긴 2차원 배열 rectangle, 초기 캐릭터의 위치 characterX, characterY, 아이템의 위치 itemX, itemY가 solution 함수의 매개변수로 주어질 때, 캐릭터가 아이템을 줍기 위해 이동해야 하는 가장 짧은 거리를 return 하도록 solution 함수를 완성해주세요.

 

제한사항
  • rectangle의 세로(행) 길이는 1 이상 4 이하입니다.
  • rectangle의 원소는 각 직사각형의 [좌측 하단 x, 좌측 하단 y, 우측 상단 x, 우측 상단 y] 좌표 형태입니다.
    • 직사각형을 나타내는 모든 좌표값은 1 이상 50 이하인 자연수입니다.
    • 서로 다른 두 직사각형의 x축 좌표, 혹은 y축 좌표가 같은 경우는 없습니다.
    • 문제에 주어진 조건에 맞는 직사각형만 입력으로 주어집니다.
  • charcterX, charcterY는 1 이상 50 이하인 자연수입니다.
    • 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.
  • itemX, itemY는 1 이상 50 이하인 자연수입니다.
    • 지형을 나타내는 다각형 테두리 위의 한 점이 주어집니다.
  • 캐릭터와 아이템의 처음 위치가 같은 경우는 없습니다.

 


 

 

2. 문제 풀이 

플레이어가 갈 수 있는 바깥의 경계선 부분과, 가지 못하는 사각형 내부를 구하는 것이 관건인 문제였다. 

겹치는 부분, 즉 'ㄷ' 자 부분에서의 오류를 피하기 위해 모든 좌표를 두 배 해주었다. 

 

1) x와 y 좌표값의 최대가50이므로, max를 2배한 값에 +1을 한 101을 MAX로 선언한다.

2) 리스트의 요소가 5( 0,1 만 아니면 ok) 로 이루어진202 x 202  2차원 리스트를 생성한다.

  • 이때, 리스트는 게임 맵을의미하여 각셀의 위치가 내부 공간인지, 사각형의 경계선인지를 표시하게 된다. 

3) 주어진 직사각형의 좌표를 2배 확장하여 경계선을 그린다. 

  • x1부터 x2까지, y1부터 y2까지 이중 반복문을 통해 각각 i와 j가 경계선인지,내부 공간인지 판단한다.
    • x1 < i < x2  /  y1 < j < y2 와 같다면 내부 공간 → 0으로 변경
    • x1 = i , x2 = i, y1 = j , y2 = j일 경우에는 경계선 → 1로 변경 

4) 현재 캐릭터의 위치, 아이템의 위치를 2배로 변경해준다.

5) 현재 캐릭터의 위치와 캐릭터가 이동한 칸수를 set()자료형으로 선언, deque 자료형에 넣는다.

6) set() 자료형으로 visited 을 선언하고, 현재 캐릭터의 위치를 visited에 추가한다.

7) 플레이어가 이동할 수 있는 방향(상하좌우)를 선언한다.

8) queue에 자료가 있을 때 까지 다음을 반복한다.

  • 큐에서 자료를 꺼내어 x, y, count를 선언한다. (x좌표, y좌표, 이동한 칸 수)
  • 만약 x와 y의 위치가 아이템의 좌표와 같다면 count에 2를 나눈 값을 반환한다.
    • 2배한 맵에서 움직였으므로 실제 이동 칸수를 구하기 위해서는 2를 나누어야 함
  • for문을 사용하여, 플레이어를 현재 위치에서 상하좌우로 이동시킨다. 이때, 플레이어가 이동하려는 칸이 MAP을 벗어나지 않고, 이동할 수 있는 칸이고, 방문한 적이 없는 칸인지를 확인한다. 3개의 조건을 만족한다면, 플레이어가 이동하려는 칸을 visited과 queue에 추가한다. 

9) 반복문이 끝났다면 -1을 return한다. 

from collections import deque
def solution(rectangle, characterX, characterY, itemX, itemY):
    MAX = 101
    grid = [[5]* (MAX*2) for _ in range(MAX*2)]
    
    for x1, y1, x2, y2 in rectangle:
        x1,y1, x2, y2 = x1*2, y1*2, x2*2, y2*2
            
        for i in range(x1, x2 + 1):
            for j in range(y1, y2 + 1):
                if x1 < i < x2 and y1 < j < y2:  
                    grid[i][j] = 0
                elif grid[i][j] != 0:  
                    grid[i][j] = 1  

                
    characterX, characterY, itemX, itemY = characterX*2, characterY*2, itemX*2, itemY*2 
    queue = deque([(characterX, characterY, 0)])
    visited = set()
    visited.add((characterX, characterY))
    
    directions = [(0, 1), (0,-1), (1, 0),(-1, 0)]
    
    while queue:
        x, y, count = queue.popleft()
        
        if (x, y) == (itemX, itemY):
            return count//2
        
        for dx, dy in directions:
            nx, ny = x+dx, y+dy
            
            if 0<=nx<MAX*2 and 0<=ny<MAX*2 and grid[nx][ny] == 1 and (nx,ny) not in visited:
                visited.add((nx, ny))
                queue.append((nx, ny, count+1))
    
    return -1

 

 

나 이런 맵...에 취약한 것 같다..

사실 bfs 부분은 게임 맵 최단 거리랑 거의 비슷한데 경계선 구하는 부분이 진짜...너무 어려웠음....

맵 문제 많이 풀어보자...^_^