본문 바로가기

PS

메이즈 러너 [삼성 기출]

1. 문제 접근 방법

- 포기하지 않는 자세와 테스트 케이스를 직접 만드는 것의 중요성을 알려준 문제이다.

-> 디버깅이 안되면 테스트 케이스를 직접 만들어야 한다 

-> 실제 제출하기 전에 테스트 케이스를 최대한 여러 개 만들어서 돌려보는 편이 매우 좋다. 

 

- 사람이 탈출하는 경우를 중간에 고려하다보니, 중간 중간에 코드를 수정해야 하는 일이 있었다.

-> 미리 코드 설계를 다 하고 나서, 그 다음에 코드를 작성하는 편이 좋을까?

-> 문제 풀이 전략에 대해 고민해봐야겠다. 

 

- rotate는 매우 중요하다.

-> rotate를 최근에 학습한 방법을 적용했는데, 에러가 발생했다.

-> 또한, rotate를 할 때도, 길이를 잘라서 rotate해야 한다는 점도 기억해야 한다.

-> 그리고 중요한 점은 그냥 행을 잘라서, 해당 열의 위치에 넣어주는 식으로 하는 편이 좋다는 점이다. 

 

- 또한, len이라는 변수를 활용하는데도 주의해야 한다.

-> 함부로 활용하면 이것이 문법 에러를 발생시킬 수 있다.

-> 최대한 활용하지 않는 편이 좋다. 

 

- 사람과 탈출구에 대한 이동과, 벽들의 이동을 분리한 것이 좋았다.

-> 사람과 탈출구에 대한 이동을 더 좋게할 수 있는 방법이 있을지 찾아봐야겠다. 

 

- 전부 탈출했는지 확인하는 시점은, 사람이 탈출하고 난 후여야 한다는 점을 반드시 기억해야 한다.

-> 왜냐하면 그렇게 하지 않으면, 다 사람이 탈출하고 나서 가장 작은 square를 찾으려고 하면 에러가 나기 때문이다. 

 

- 중간에 코드를 수정할 때, 특히 for문의 변수명 같은 것을 특히 주의해야 한다.

-> 이러한 부분은 매우 실수하기 쉬운 부분이다. 

 

 

 

2. 코드

import copy 

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

board = [] 

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

for i in range(N):
    row = list(map(int, input().split()))
    board.append(row)
    
    
    
people = []
totalMove = 0 

exit = [False]*M 

for i in range(M):
    a, b = map(int, input().split())
    people.append([a-1, b-1])


ox, oy = map(int, input().split()) 
ox -= 1
oy -= 1 

## 사람들을 이동시키는 함수
## 사람들을 한 명씩 순차적으로, 이동시키되, 
## 탈출을 만나면 바로 탈출시킨다. 
## 한 번 탈출한 사람은 이동시키지 않는다. 
def movePeople():
    
    
    global totalMove 
    
     
    ## people 배열을 탐색한다. 
    for i in range(len(people)):
        if exit[i] == True:
            continue
        
        move = 0
        
        x = people[i][0]
        y = people[i][1] 
        
        ## 탈출구까지의 거리를 계산한다 
        currDist = abs(x-ox)+abs(y-oy)
        
        nextMove = False
        
        ## 4방향에 대해서 움직였을 때, 
        ## 현재 거리(탈출구까지의 거리)보다 작아지는지 확인한다.
        ## 이 때, 이동하는 과정에서 탈출구를 만나면 바로 탈출시킨다. 
        ## 단, move의 카운트는 세줘야 한다. 
        for j in range(4):
            nx = x + dx[j]
            ny = y + dy[j]
            
            ## board안에 있고, 빈 칸일때만 이동 가능하다. 
            if 0<=nx and nx < N and 0<=ny and ny<N and board[nx][ny] == 0:
                
                nextDist = abs(nx-ox)+abs(ny-oy)
                
                if nx == ox and ny == oy:
                    exit[i] = True
                    move = 1 
                    break
                
                ## move를 중복하지 않도록 nextMove라는 변수로 중복을 방지하였다. 
                if nextMove == False and currDist > nextDist:
                    people[i][0] = nx
                    people[i][1] = ny
                    move = 1 
                    nextMove = True

        ## totalMove에 move를 더해준다. 
        ## 이 때, move를 중복해서 세지 않도록 주의해야 한다. 
        totalMove += move 
        

## 가장 작은 정사각형을 찾는 함수 
def findSmallestSquare():
    

    ## 정사각형의 변의 길이를 1부터 N까지 늘려주면서, 탐색한다
    ## 하나라도 맞는 케이스가 나오면, 바로 해당 sx,sy,ex,ey 좌표를 리턴시킨다. 
    ## 정사각형의 네 점을 찾는 것이 목표이다. 
    ## for문의 변수명으로 len과 같은 것은 쓰지 않도록 주의한다. 
    for k in range(1, N+1):
        for i in range(0, N-k+1):
            for j in range(0, N-k+1):
                
                sx = i
                sy = j
                ex = i+k-1
                ey = j+k-1
                
                ## 탈출문이 현재 내가 설정한 sx,sy,ex,ey 내부에 있을 때, 
                if sx<= ox and ox <=ex and sy<= oy and oy <= ey:
                    ## 마찬가지로 for문의 변수명을 주의한다 
                    ## 탈출하지 않은 사람중에서 한 사람이라도 구간 내에 있다면
                    ## 유효하므로 바로 리턴한다. 
                    for l in range(len(people)):
                        if exit[l] == True:
                            continue
                        
                        x = people[l][0]
                        y = people[l][1] 
                    
                        if sx<= x and x <= ex and sy <= y and y <= ey:
                            return [sx, sy, ex, ey]
             
    
## square를 rotate 시킨다
def rotate(square):
    
    
    ## print("square는?")
    ## print(square)
    
    ## tmpBoard를 만들어준다. 
    tmpBoard = copy.deepcopy(board)
    
    ## square의 sx, sy, ex, ey 네 점을 변수에 저장한다. 
    sx = square[0]
    sy = square[1]
    ex = square[2]
    ey = square[3] 
    
    
    squareLen = ex-sx+1 
    col = ey 
    
    
    ## 행을 row라는 리스트에 담은 다음에
    ## 해당하는 열에 하나씩 담아준다. 
    for i in range(sx, ex+1):
        
        row = board[i][sy:ey+1]
        pos = 0 
        
        for j in range(sx, ex+1):
            tmpBoard[j][col] = row[pos]
            pos += 1 
            
        col -= 1 
            
            
    ## tmpBoard를 내가 변경한 구간만,
    ## board에 업데이트해준다. 
    for i in range(sx, ex+1):
        for j in range(sy, ey+1):
            board[i][j] = tmpBoard[i][j] 
            
            
  
    
    
## 사람과 탈출구를 rotate하기 위한 함수이다. 
def rotatePeopleAndOut(square):
    
    
    global ox
    global oy
    
    global people 
    
    tmpBoard = copy.deepcopy(board)
    initBoard = copy.deepcopy(board)
    
    sx = square[0]
    sy = square[1]
    ex = square[2]
    ey = square[3]
    
    
    ## initBoard에 해당 구간에 있는 사람을 
    ## 숫자로 만들어서 넣어준다. 
    for i in range(len(people)):
        if exit[i] == True:
            continue
        
        x = people[i][0]
        y = people[i][1] 
    
        if sx<= x and x <= ex and sy <= y and y<=ey:
            num = 100*(x+1)+y+1
            initBoard[x][y] = num

    oNum = 100*(ox+1)+oy+1
    initBoard[ox][oy] = oNum
    
    squareLen = ex-sx+1 
   
    col = ey 
    
    ## 마찬가지로 initBoard의 행을 90도 변환시켜서
    ## tmpBoard에 업데이트해준다. 
    for i in range(sx, ex+1):
        
        row = initBoard[i][sy:ey+1]
        
        pos = 0 
        
        for j in range(sx, ex+1):
            tmpBoard[j][col] = row[pos]
            pos += 1 
            
        col -= 1 
   
   

   
    ## people에 있는 값들 중에서
    ## tmpBoard에 업데이트되어 있는 값들을 찾아서, 
    ## 일치한다면, 해당 위치로, people의 인덱스를 바꿔준다.
    ## 이렇게 함으로써, people은 board에 따로 표시하지 않고도 
    ## 위치를 바꿔줄 수 있다. 
    for i in range(len(people)):
        if exit[i] == True:
            continue 
        
        x = people[i][0]
        y = people[i][1]
        
        if sx<=x and x <= ex and sy<=y and y <= ey:
            num = 100*(x+1)+y+1 
            
            for j in range(0, N):
                for k in range(0, N):
                    if tmpBoard[j][k] == num:
                        people[i][0] = j
                        people[i][1] = k
    
    
    ## 마찬가지로 탈출구도 같은 방식으로 정보를 업데이트한다.  
    for i in range(0, N):
        for j in range(0, N):
            if tmpBoard[i][j] == oNum:
                ox = i
                oy = j 
                
            
            
## 벽을 1만큼 부수는 함수
## 해당하는 범위에서 벽이 있다면, 1씩 부숴준다. 
def breakWall(square):
    
    sx = square[0]
    sy = square[1]
    ex = square[2]
    ey = square[3]
    
    
    for i in range(sx, ex+1):
        for j in range(sy, ey+1):
            if board[i][j] > 0:
                board[i][j] -= 1 
    
    
            
    
time = 0 
    
while True:     
    
    ## 시간을 1 증가시킨다.
    ## 시간을 선제적으로 증가시키는 것이 필요하다.
    ## 왜냐하면 그래야 중간에 break문을 할 때, 시간이 제대로 나오기 때문이다. 
    time +=1 
    
    ## 사람들을 이동시킨다. 
    movePeople()     
    
    cnt = 0 
    
    for i in range(len(people)):
        if exit[i] == True:
            cnt += 1 
    
    ## 모든 사람이 탈출했다면, 바로 break해주는 것이 중요하다. 
    ## 여기서 break하지 않으면, findSmallestSquare에서 오류가 발생한다. 
    if cnt == len(people):
        break 
    
                
    square = findSmallestSquare()
    
    rotate(square)
    
    rotatePeopleAndOut(square)
    
    
    breakWall(square) 
  
    
    ## 시간이 K에 도달하면 바로 break해준다.      
    if time == K:
        break
    
    
    
print(totalMove)
print(ox+1, oy+1)

 

3. 참고 코드

n, m, k = tuple(map(int, input().split()))
# 모든 벽들의 상태를 관리한다. 
board = [
    [0] * (n + 1)
    for _ in range(n + 1)
]

for i in range(1, n + 1):
    board[i] = [0] + list(map(int, input().split()))

# 회전의 구현을 편리하게 하기 위해 2차원 배열을 만들어준다. 
next_board = [
    [0] * (n + 1)
    for _ in range(n + 1)
]

# 참가자의 위치 정보를 기록해줍니다.
traveler = [(-1, -1)] + [
    tuple(map(int, input().split()))
    for _ in range(m)
]

# 출구의 위치 정보를 기록합니다. 
exits = tuple(map(int, input().split()))

# 정답(모든 참가자들의 이동 거리 합)을 기록해줍니다.
ans = 0

# 회전해야 하는 최소 정사각형을 찾아 기록해줍니다.
sx, sy, square_size = 0, 0, 0


# 모든 참가자를 이동시킵니다. 
def move_all_traveler():
    global exits, ans

    # m명의 모든 참가자들에 대해 이동을 진행합니다.
    for i in range(1, m + 1):
        # 이미 출구에 있는 경우 스킵합니다.
        if traveler[i] == exits:
            continue
        
        tx, ty = traveler[i]
        ex, ey = exits

        # 행이 다른 경우 행을 이동시켜봅니다.
        if tx != ex:
            nx, ny = tx, ty

            if ex > nx: 
                nx += 1
            else:
                nx -= 1

            # 벽이 없다면 행을 이동시킬 수 있습니다.
            # 이 경우 행을 이동시키고 바로 다음 참가자로 넘어갑니다.
            if not board[nx][ny]:
                traveler[i] = (nx, ny)
                ans += 1
                continue

        # 열이 다른 경우 열을 이동시켜봅니다.
        if ty != ey:
            nx, ny = tx, ty

            if ey > ny: 
                ny += 1
            else:
                ny -= 1

            # 벽이 없다면 행을 이동시킬 수 있습니다.
            # 이 경우 열을 이동시킵니다.
            if not board[nx][ny]:
                traveler[i] = (nx, ny)
                ans += 1
                continue


# 한 명 이상의 참가자와 출구를 포함한 가장 작은 정사각형을 찾습니다.
def find_minimum_square():
    global exits, sx, sy, square_size
    ex, ey = exits

    # 가장 작은 사각형부터 모든 사각형을 만들어봅니다. .
    for sz in range(2, n + 1):
        # 가장 좌상단 좌표 r이 작은 것부터 하나씩 만들어봅니다.
        for x1 in range(1, n - sz + 2):
            # 가장 좌상단 좌표 c가 작은 것부터 하나씩 만들어봅니다.
            for y1 in range(1, n - sz + 2):
                x2, y2 = x1 + sz - 1, y1 + sz - 1

                # 만약 출구가 해당 정사각형 안에 없다면 스킵합니다. 
                if not (x1 <= ex and ex <= x2 and y1 <= ey and ey <= y2):
                    continue

                # 한 명 이상의 참가자가 해당 정사각형 안에 있는지 판단합니다.
                is_traveler_in = False
                for l in range(1, m + 1):
                    tx, ty = traveler[l]
                    if x1 <= tx and tx <= x2 and y1 <= ty and ty <= y2:
                        # 출구에 있는 참가자는 제외합니다.
                        if not (tx == ex and ty == ey):
                            is_traveler_in = True

                # 만약 한 명 이상의 참가자가 있다면,
                # 정보를 업데이트합니다. 
                if is_traveler_in:
                    sx = x1
                    sy = y1
                    square_size = sz

                    return


# 정사각형 내부의 벽을 회전시킵니다.
def rotate_square():
    # 우선 정사각형 안에 있는 벽들을 1 감소시킵니다.
    for x in range(sx, sx + square_size):
        for y in range(sy, sy + square_size):
            if board[x][y]: 
                board[x][y] -= 1

    # 정사각형을 시계방향으로 90' 회전합니다.
    for x in range(sx, sx + square_size):
        for y in range(sy, sy + square_size):
            # Step 1. (sx, sy)를 (0, 0)으로 옮겨주는 변환을 진행합니다. 
            ox, oy = x - sx, y - sy
            # Step 2. 변환된 상태에서 90도 회전시킵니다. 
            rx, ry = oy, square_size - ox - 1
            # Step 3. 다시 (sx, sy)를 더해줍니다. 
            next_board[rx + sx][ry + sy] = board[x][y]

    # next_board 값을 현재 board에 옮겨줍니다.  
    for x in range(sx, sx + square_size):
        for y in range(sy, sy + square_size):
            board[x][y] = next_board[x][y]


# 모든 참가자들 및 출구를 회전시킵니다.
def rotate_traveler_and_exit():
    global exits

    # m명의 참가자들을 모두 확인합니다.
    for i in range(1, m + 1):
        tx, ty = traveler[i]
        # 해당 참가자가 정사각형 안에 포함되어 있을 때에만 회전시킵니다. 
        if sx <= tx and tx < sx + square_size and sy <= ty and ty < sy + square_size:
            # Step 1. (sx, sy)를 (0, 0)으로 옮겨주는 변환을 진행합니다. 
            ox, oy = tx - sx, ty - sy
            # Step 2. 변환된 상태에서는 회전 이후의 좌표가 (x, y) . (y, square_n - x - 1)가 됩니다.
            rx, ry = oy, square_size - ox - 1
            # Step 3. 다시 (sx, sy)를 더해줍니다.
            traveler[i] = (rx + sx, ry + sy)

    # 출구에도 회전을 진행합니다.
    ex, ey = exits
    if sx <= ex and ex < sx + square_size and sy <= ey and ey < sy + square_size:
        # Step 1. (sx, sy)를 (0, 0)으로 옮기는 변환을 진행합니다.  
        ox, oy = ex - sx, ey - sy
        # Step 2. 변환된 상태에서는 회전 이후의 좌표가 
        #(x,y) -> (y-square_n-x-1)이 됩니다.
        rx, ry = oy, square_size - ox - 1
        # Step 3. 다시 (sx, sy)를 더해줍니다. 
        exits = (rx + sx, ry + sy)


for _ in range(k):
    # 모든 참가자를 이동시킵니다. 
    move_all_traveler()

    # 모든 사람이 출구로 탈출했는지 판단합니다.
    is_all_escaped = True
    for i in range(1, m + 1):
        if traveler[i] != exits:
            is_all_escaped = False

    # 만약 모든 사람이 출구로 탈출했으면 바로 종료합니다.
    if is_all_escaped: 
        break

    # 한 명 이상의 참가자와 출구를 포함한 가장 작은 정사각형을 찾습니다.
    find_minimum_square()

    # 정사각형 내부의 벽을 회전시킵니다.
    rotate_square()
    # 모든 참가자들 및 출구를 회전시킵니다.
    rotate_traveler_and_exit()

print(ans)

ex, ey = exits
print(ex, ey)

'PS' 카테고리의 다른 글

이분 그래프 [백준 1707]  (1) 2023.10.10
마법사 상어와 블리자드 [삼성 기출]  (1) 2023.10.10
N-queen [백준 9663]  (0) 2023.10.09
연속합 [백준 1912]  (1) 2023.10.09
마법사 상어와 파이어 스톰 [삼성 기출]  (1) 2023.10.08