본문 바로가기
공부-코딩테스트/코테풀이 - 자바, 파이썬

코딩테스트 문제 : 거리두기 확인하기 (프로그래머스)

by 령과 2022. 1. 13.

문제

https://programmers.co.kr/learn/courses/30/lessons/81302

 

코딩테스트 연습 - 거리두기 확인하기

[["POOOP", "OXXOX", "OPXPX", "OOXOX", "POXXP"], ["POOPX", "OXPXP", "PXXXO", "OXXXO", "OOOPP"], ["PXOPX", "OXOXP", "OXPOX", "OXXOP", "PXPOX"], ["OOOXX", "XOOOX", "OOOXX", "OXOOX", "OOOOO"], ["PXPXP", "XPXPX", "PXPXP", "XPXPX", "PXPXP"]] [1, 0, 1, 1, 1]

programmers.co.kr

 

나의 코드

import collections
ways = [[0,-1],[1,0],[0,1],[-1,0]]

def solution(places):
    answer = []
    for place in places:
        check = 1                       #거리두기를 잘한다는 가정에서 시작한다.
        for x in range(5):              #places에서 대기실 하나를 불러온다.
            for y in range(5):          #대기실에서 P를 찾는다.
                if place[x][y] == "P":  #P를 찾았으면 bfs함수를 호출한다.
                    check = min(bfs(place.copy(),x,y),check)
                    #거리두기를 하지 않는 사람이 있었다면 계속 bfs유무와 상관없이 0이 선택된다. 
                    
        answer.append(check) #대기실 결과를 answer에 추가한다.
    return answer

def bfs(place, x, y):
    dq = collections.deque([[x,y,0]]) #처음 시작할 때 P의 위치를 가진 dequeue를 생성한다.
    init_p = list(place[x])           #자기 자신을 위치에서 맴돌지 않도록 처음 P를 X처리한다.
    init_p[y] = "X"                   #처음 위치 P를 X로 처리해도 상관없다.
    place[x] = "".join(init_p)
    while dq:                         #bfs 시작
        target = dq.popleft()         #dq에서 맨 앞 요소를 pop한다.
        
        if place[target[0]][target[1]] == "P" and target[2] != 0:#탐색중에 P를 만나면 바로 종료
            return 0                                             #target[2]==0이면 자기자신이란 뜻
        
        if target[2] < 2:             #세 번이상 이동 한 경우를 생각할 필요가 없다. 두번 이동한 것 까지에서 멈춘다.
            for way in ways:          #이동할 수 있는 방향 4가지를 모두 확인하여 이동가능하면 queue에 추가한다.
                if target[2] <= 1:    #아직 이동 횟수가 1번이라면 더 이동할 수 있다.
                    i = target[0] + way[0]
                    j = target[1] + way[1]
                    
                    if out_check(i,j):      #index범위를 넘어간 경우는 제외해야 한다.
                        if place[i][j] != "X": #가고자 하는 방향이 막혀있지 않다면 queue에 추가한다.
                            dq.append([i,j,target[2] + 1])
                            
                            if place[i][j] == "O": #이동하였으면 다른 탐색에서 이동한 방향으로 오지 못하도록 X처리한다.
                                tmp = list(place[i])
                                tmp[j] = "X"
                                place[i] = "".join(tmp)
    return 1

def out_check(x,y): #범위를 넘어갔는지 확인하는 함수
    if 0 <= x < 5 and 0 <= y < 5:
        return True
    else:
        return False

P에서 시작해서 2번 움직인 경로까지에 또 다른 P가 있다면 0 없다면 1을 표시하면 된다고 생각했다.

제한된 깊이에서 탐색을 하는데 BFS를 이용하였고 [행, 열, 깊이] 형식으로 Queue에 추가하며 처리하였다.

dq에서 각각의 요소 [x, y, d]는 x,y좌표까지 오는데 d번 움직여야 한다는 의미이다.

좌표를 dq에 넣을 때 지나왔다는 표시로 X로 바꾸는데 기존의 대기실에 영향을 주지 않기 위해 copy()를 인수로 주었다.

 

print로 상황을 볼 수 있는 코드: 아래 코드를 복사 후 실행하면 상황을 볼 수 있다.

import collections
ways = [[0,-1],[1,0],[0,1],[-1,0]]

def solution(places):
    answer = []
    for place in places:
        print("{}대기실 탐색시작".format(place))
        check = 1                       #거리두기를 잘한다는 가정에서 시작한다.
        for x in range(5):              #places에서 대기실 하나를 불러온다.
            for y in range(5):          #대기실에서 P를 찾는다.
                if place[x][y] == "P":  #P를 찾았으면 bfs함수를 호출한다.
                    print("{}, {}좌표를 확인합니다".format(x,y))
                    check = min(bfs(place.copy(),x,y),check)
                    #거리두기를 하지 않는 사람이 있었다면 계속 bfs유무와 상관없이 0이 선택된다. 
        print("----------------------------------------------------------------")
                    
        answer.append(check) #대기실 결과를 answer에 추가한다.
    return answer

def bfs(place, x, y):
    dq = collections.deque([[x,y,0]]) #처음 시작할 때 P의 위치를 가진 dequeue를 생성한다.
    init_p = list(place[x])           #자기 자신을 위치에서 맴돌지 않도록 처음 P를 X처리한다.
    init_p[y] = "X"                   #처음 위치 P를 X로 처리해도 상관없다.
    place[x] = "".join(init_p)
    while dq:                         #bfs 시작
        target = dq.popleft()         #dq에서 맨 앞 요소를 pop한다.
        print("{}, {}좌표 : {}이동했을 때의 상황에서 시작".format(target[0],target[1],target[2]))
        if place[target[0]][target[1]] == "P" and target[2] != 0:
            return 0
        
        print_tmp=[]
        
        if target[2] < 2:             #세 번이상 이동 한 경우를 생각할 필요가 없다. 두번 이동한 것 까지에서 멈춘다.
            for way in ways:          #이동할 수 있는 방향 4가지를 모두 확인하여 이동가능하면 queue에 추가한다.
                if target[2] <= 1:    #아직 이동 횟수가 1번이라면 더 이동할 수 있다.
                    i = target[0] + way[0]
                    j = target[1] + way[1]
                    
                    if out_check(i,j):      #index범위를 넘어간 경우는 제외해야 한다.
                        if place[i][j] != "X": #가고자 하는 방향이 막혀있지 않다면 queue에 추가한다.
                            dq.append([i,j,target[2] + 1])
                            print_tmp.append([i,j])
                            
                            if place[i][j] == "O": #이동하였으면 다른 탐색에서 이동한 방향으로 오지 못하도록 X처리한다.
                                tmp = list(place[i])
                                tmp[j] = "X"
                                place[i] = "".join(tmp)
            if len(print_tmp)!=0:
                print("탐색 결과 {}좌표들로 이동가능 함을 확인".format(print_tmp))
            print("탐색 후 dq상태: {}".format(dq))
            print("")
    return 1

def out_check(x,y): #범위를 넘어갔는지 확인하는 함수
    if 0 <= x < 5 and 0 <= y < 5:
        return True
    else:
        return False

댓글