[PS, Algorithm] - 거리두기 확인하기 (2021 카카오 채용연계형 인턴십, LEVEL 2)

조재현·2022년 11월 27일
0

📒문제



📌정답 풀이

나름 BFS/DFS 공부는 정말 열심히 했기 때문에 이 문제도 딱 보자마자 BFS/DFS로 푸는 문제라는 생각이 들었다. 두 참석자가 대각선으로 위치할 때 파티션이 있는지 없는지 판단하는 데서 조금 버벅이긴 했지만 결국 문제는 풀 수 있었다.

이 코드는 어쨌거나 정답 코드이므로 조금 설명을 자세히 써볼까 한다.

from collections import deque

def solution(places): 
    def bfs(r, c):
        q = deque() #BFS를 수행할 Queue
        visit = [[False for _ in range(5)] for _ in range(5)]
        
        #BFS를 수행할 방향을 미리 설정
        d_r = [1, -1, 0, 0]
        d_c = [0, 0, 1, -1]
        
        #시작점은 (r,c)이므로, queue에 넣고 visit[r][c]=True
        q.append((r, c))
        visit[r][c] = True
        
        while(q):
            T = q.popleft()
            c_r, c_c = T[0], T[1]
            visit[c_r][c_c] = True
            #BFS 수행하며 queue에서 pop하고, pop한 위치에 대해선 visit = True수행
            
            
            for i in range(4):
                n_r = c_r + d_r[i]
                n_c = c_c + d_c[i]
                # BFS 수행할 방향을 더해줌
                
                if n_r < 0 or n_c < 0 or n_r > 4 or n_c > 4: continue # 맵밖을 나간다면 조건에 맞지 않으므로 제외
                if visit[n_r][n_c] == True: continue # 이미 방문한 위치라면 굳이 더 방문할 이유 없으므로 제외
                
                if abs(n_r-r) + abs(n_c-c) <= 2 and room[n_r][n_c] == 'P': 
                # 만일 맨해튼 거리가 2이하라면, 파티션이 중간에 가로막고 있는지 판단해야 함. 
                # 가로 막고 있다면 거리두기를 지킨 거지만, 그렇지 않다면 지키지 않은 것임.
                    if n_r == r or n_c == c:
                        if room[(n_r+r)//2][(n_c+c)//2] != 'X': return False
                    # 만일 행이나 열 중 하나가 같다면 (가로, 세로로 두 참석자가 배열되어 있다면)
                    # 그 중간은 무조건 파티션이어야만 거리두기를 지킬 수 있음.
                    else:
                        if room[n_r][c] != 'X' or room[r][n_c] != 'X': return False
                	# 만일 행과 열이 전부 다르다면 이 두 참석자는 대각선으로 배열되어 있는 상태
                    # 2*2에서 참석자를 제외한 2칸은 모두 파티션이어야 거리두기를 지킬 수 있음.
                elif room[n_r][n_c] == 'O':
                    q.append((n_r, n_c))
                    # 'O'라면 BFS의 길이므로 queue에 append
                    
        return True
                    
                    
    result = []

    for place in places:
        room = []
        
        for p in place:
            room.append(list(p))

        s = True
        for i in range(5):
            for j in range(5): #이중 for문을 돌면서 해당 요소가 참석자라면, 그 위치부터 BFS 수행
                if room[i][j] == 'P':
                    s = bfs(i, j)&s
                    
        if s: result.append(1)
        else: result.append(0) 
            
    return result

이중 for문을 통해 방을 돌면서 만일 해당 요소가 참석자라면 그 위치로부터 bfs를 수행한다.

자세한 설명은 주석에 달아 놓는다.

📌모범 풀이

내가 이 문제를 버벅였던 이유는 대각선으로 위치한 참석자들의 조건을 전부 고려하지 못했기 때문이다. 그래서 당연히 대각선에 위치한 참석자를 고려하는게 관건이라고 생각했던 문젠데, 인터넷 풀이를 보니 대각선을 오히려 고려하지 않는게 더 쉽게 풀 수 있다고 한다.

# 출처: https://velog.io/@hammii/ velog참조

from collections import deque

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


def bfs(place, x, y):
    visited = [[False] * 5 for _ in range(5)]
    q = deque()
    q.append((x, y, 0))
    visited[x][y] = True

    while q:
        x, y, cost = q.popleft() #이렇게도 쓸 수 있는 건 처음 알았다...
        if cost == 2:
            continue
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0 <= nx < 5 and 0 <= ny < 5:
                if visited[nx][ny]:
                    continue
                # cost==1 지점에서 다음으로 가는 과정에서 미리 거리두기 준수 여부를 판단. 
                if place[nx][ny] == 'P':
                    return False
                if place[nx][ny] == 'O':
                    q.append((nx, ny, cost + 1))
                    visited[nx][ny] = True
    return True


def solution(places):
    answer = []

    for place in places:
        p = [list(place[i]) for i in range(5)]	# 대기실
        flag = True

        for i in range(5):
            for j in range(5):
                if p[i][j] == 'P':
                    if not bfs(p, i, j):  # 거리두기 안 지켰을 경우
                        flag = False
            if not flag:
                break
        if flag:
            answer.append(1)
        else:
            answer.append(0)

    return answer

이 코드는 거리까지 bfs queue에 담았다.

  • q에서 pop한 정점이 cost==2일때, 이 지점은 빈 책상이라는 것을 암묵적으로 의미한다. (queue에 append되는 때는 빈 책상일때 뿐이기 때문)

  • 이때 여기서 continue해주는 부분은, 이 이상 BFS를 수행한다면 이후 방문하는 지점들은 무조건 맨해튼 거리가 2를 넘으므로, 그 지점이 X이든, O이든, P이든 거리두기 준수 여부에 영향을 미치지 않는다.

  • 이 코드는 cost==1 지점에서 다음으로 가는 과정에서 미리 거리두기 준수 여부를 판단한다. 따라서 대각선으로 참석자들이 위치할 때 cost==1지점에서 그 다음 지점에 'P'가 있다면 이는 거리두기 미준수로 볼 수 있다.

  • 만일 대각선으로 위치한 참석자 사이가 모두 파티션으로 막혀있다면, 맨해튼 거리(절대적인 거리)는 2지만 애초에 'O'인 지점만을 방문하기 때문에 cost==2보다 멀리 있는 지점은 애초에 탐색조차 하지 않는다.

  • 따라서 막혀있다면 아예 고려하지 않고, 안 막혀있다면 다음 지점에서 탐색을 해주기 때문에 대각선을 굳이 따로 생각해 줄 이유가 없다.

profile
꿈이 많은 개발자 지망생

0개의 댓글