[프로그래머스] LEVEL0 안전지대 파이썬

그린·2023년 2월 18일
0

프로그래머스

목록 보기
11/28
post-thumbnail

[프로그래머스] LEVEL0 안전지대


✔️문제

✅문제 설명

다음 그림과 같이 지뢰가 있는 지역과 지뢰에 인접한 위, 아래, 좌, 우 대각선 칸을 모두 위험지역으로 분류합니다.

지뢰는 2차원 배열 board에 1로 표시되어 있고 board에는 지뢰가 매설 된 지역 1과, 지뢰가 없는 지역 0만 존재합니다.
지뢰가 매설된 지역의 지도 board가 매개변수로 주어질 때, 안전한 지역의 칸 수를 return하도록 solution 함수를 완성해주세요.

✅제한사항

board는 n * n 배열입니다.
1 ≤ n ≤ 100
지뢰는 1로 표시되어 있습니다.
board에는 지뢰가 있는 지역 1과 지뢰가 없는 지역 0만 존재합니다.


🙋‍♀️내 풀이

  1. 테스트 케이스 1,4,5,7,11번에 실패한 코드
def solution(board):
    answer = 0
    for i in range(len(board)) :
        for j in range(len(board)) :
            if board[i][j] == 1 :
                if 0<i<len(board)-1 and 0<j<len(board)-1:
                    board[i][j-1]=2 if board[i][j-1] == 0 else board[i][j-1]
                    board[i][j+1]=2 if board[i][j+1] == 0 else board[i][j+1]
                    board[i-1][j]=2 if board[i-1][j] == 0 else board[i-1][j]
                    board[i+1][j]=2 if board[i+1][j] == 0 else board[i+1][j]
                    board[i-1][j-1]=3 if board[i-1][j-1] == 0 else board[i-1][j-1]
                    board[i-1][j+1]=3 if board[i-1][j+1] == 0 else board[i-1][j+1]
                    board[i+1][j+1]=3 if board[i+1][j+1] == 0 else board[i+1][j+1]
                    board[i+1][j-1]=3 if board[i+1][j-1] == 0 else board[i+1][j-1]
    for i in range(len(board)) :
        for j in range(len(board)) :
            if board[i][j] == 0:
                answer += 1
    print(board)
    return answer
  1. 지뢰가 있는 곳을 발견하면 if board[i][j] == 1 :
  2. 위험지역의 위, 아래, 좌우의 값은 2로, 대각선의 값은 3으로 바꾼다.
    2-1. 단, 위험지역의 위, 아래, 좌우, 대각선의 값이 0이 아니라면 (지뢰가 있는 지역이거나 이미 다른 지뢰의 인접지역으로 값이 바뀐 곳이라면) 그 값을 그대로 둔다.

i와 j가 인덱스에서 벗어나지 않게 하기 위해
0<i<len(board)-1 and 0<j<len(board)-1 이 조건을 사용해서 (0, 0) 에 지뢰가 있는 경우의 위험지역을 세지 못했다.


위의 오류를 수정한 두번째 코드이다.

def solution(board):
    answer = []
    for i in range(len(board)) :
        for j in range(len(board)) :
            if board[i][j] == 1 :
                answer.append([i, j])
                answer.append([i, j+1])
                answer.append([i, j-1])
                answer.append([i-1, j])
                answer.append([i+1, j])
                answer.append([i+1, j+1])
                answer.append([i-1, j-1])
                answer.append([i+1, j-1])
                answer.append([i-1, j+1])
    print(len(answer))
    answer = [i for i in set(answer) if 0<i[0]<len(board[0]) and 0<i[1]<len(board[1])]
    print(answer)
    return len(board)**2 - len(answer)

지뢰가 있는 좌표의 위, 아래, 좌우, 그리고 대각선의 모든 좌표를 answer 배열에 추가하고, 인덱스 범위가 아닌 것은 나중에 제외시키는 방법이다.

위 코드를 실행시키니까 다음과 같은 오류가 발생했다.

TypeError: unhashable type: 'list'

위 오류는 answer의 원소가 list이기 때문이다. set()에 들어있는 값은 해쉬가 가능해야 한다. 즉, 문자열, 숫자, 튜플만을 인자로 받을 수 있다.

def solution(board):
    answer = []
    for i in range(len(board)) :
        for j in range(len(board)) :
            if board[i][j] == 1 :
                answer.append((i, j))
                answer.append((i, j+1))
                answer.append((i, j-1))
                answer.append((i-1, j))
                answer.append((i+1, j))
                answer.append((i+1, j+1))
                answer.append((i-1, j-1))
                answer.append((i+1, j-1))
                answer.append((i-1, j+1))
    
    answer = [i for i in set(answer) if 0<=i[0]<len(board[0]) and 0<=i[1]<len(board[1])]
    
    return len(board)**2 - len(answer)

위 오류를 해결하기 위해 answer.append([i, j])answer.append((i, j))로 수정했더니 테스트 케이스 9번빼고 통과되었다.
테스트 케이스 9번은 런타임 오류가 났다. 런타임 오류를 해결하기 위해
answer = [i for i in set(answer) if 0<=i[0]<len(board[0]) and 0<=i[1]<len(board[1])]
위 코드를
answer = [i for i in set(answer) if 0<=i[0]<len(board) and 0<=i[1]<len(board)]
위와 같이 수정하였다.


🤔다른 사람의 풀이

  1. 생각하지 못한 아이디어가 있는 코드
def solution(board):
    answer = 0

    for col in range(len(board)):
        for row in range(len(board[col])):
            if board[row][col] == 1:
                for j in range(max(col-1,0),min(col+2,len(board))):
                    for i in range(max(row-1,0),min(row+2,len(board))):
                        if board[i][j] == 1:
                            continue
                        board[i][j] = -1
    for i in board:
        answer += i.count(0)

    return answer

max와 min으로 범위를 벗어나지 않게 해줬다.
1. 지뢰에 해당하는 ( i , j ) 좌표를 만나면 i-1부터 i+1 그리고 j-1부터 j+1의 모든 조합이 위험지역이므로 해당 좌표들의 값을 1로 바꿔준다.
1-1. 단, i-1, j-1의 값이 0보다 작아지는 경우와 i+1, j+1의 값이 마지막 인덱스를 넘어가는 경우를 min과 max 함수를 사용하여 처리했다.


2. 짧은 코드

def solution(board):
    n = len(board)
    danger = set()
    for i, row in enumerate(board):
        for j, x in enumerate(row):
            if not x:
                continue
            danger.update((i+di, j+dj) for di in [-1,0,1] for dj in [-1, 0, 1])
    return n*n - sum(0 <= i < n and 0 <= j < n for i, j in danger)
  1. if not x: 코드를 사용하여 x가 0이면 그냥 지나가도록 했다.
  2. danger.update((i+di, j+dj) for di in [-1,0,1] for dj in [-1, 0, 1])
    위험지역의 모든 경우의 수를 하나하나 적어서 계산해준 내 코드와 다르게 반복문을 사용해 모든 조합을 계산할 수 있도록 했다.

👍오늘의 배운 점

💡set() 함수

set()에 들어있는 값은 해쉬가 가능해야 한다.
즉, set() 함수는 인자로 문자열, 숫자, 튜플만을 받을 수 있다.


배열의 인덱스를 이용해 문제를 풀 때, min(), max() 함수를 활용해 예외처리를 하는 것도 한가지 방법이다.

0개의 댓글