Test/python

안전지대 (enumerate)

soo15 2023. 4. 22. 20:25

[문제 설명]

다음 그림과 같이 지뢰가 있는 지역과 지뢰에 인접한 

위, 아래, 좌, 우 대각선 칸을 모두 위험지역으로 분류합니다.

지뢰는 2차원 배열 board에 1로 표시되어 있고 board에는

지뢰가 매설 된 지역 1과, 지뢰가 없는 지역 0만 존재합니다.
지뢰가 매설된 지역의 지도 board가 매개변수로 주어질 때, 

안전한 지역의 칸 수를 return하도록 solution 함수를 완성해주세요.


[제한사항]

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

 

[입출력 예]

board  result
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0]] 16
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 1, 0], [0, 0, 0, 0, 0]] 13
[[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]] 0

 

[입출력 예 설명]

입출력 예 #1)
(3, 2)에 지뢰가 있으므로 지뢰가 있는 지역과 지뢰와 인접한 

위, 아래, 좌, 우, 대각선 총 8칸은 위험지역입니다. 

따라서 16을 return합니다.


입출력 예 #2)
(3, 2), (3, 3)에 지뢰가 있으므로 지뢰가 있는 지역과 지뢰와 인접한 

위, 아래, 좌, 우, 대각선은 위험지역입니다. 

따라서 위험지역을 제외한 칸 수 13을 return합니다.


입출력 예 #3)
모든 지역에 지뢰가 있으므로 안전지역은 없습니다. 

따라서 0을 return합니다.

 


[나의 풀이]

import numpy as np       

def solution(board):
	
    # 1. board를 matrix로 만들기
    a = np.array(board)
	
    # 2. 지뢰가 있는 위치를 list로 만들기
    list_i = []
    list_j = []
    n_0 = a.shape[0]
    n_1 = a.shape[1]

    for i in range(0, n_0):
        for j in range(0, n_1):
            if a[i][j] == 1:
                list_i.append(i)
                list_j.append(j)
	
    # 3. 지뢰가 있는 곳의 좌,우,상,하,대각선에 1 넣기
    for i, j in zip(list_i, list_j):
        a[max(i-1, 0):min(i+2, n_0),  max(j-1, 0):min(j+2, n_1)] = 1
	
    # 4. 1의 개수 세기
    count = np.count_nonzero(a == 1)
	
    # 5. 0의 개수 세기
    return a.shape[0]*a.shape[1] - count

 


[설명]

1. board를 matrix로 만들기

import numpy as np

board = [[1, 0, 0, 0, 1], [0, 1, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [1, 0, 0, 0, 1]]
a = np.array(board)
print(a)

Out[1] :

[[1 0 0 0 1]

 [0 1 0 0 0]

 [0 0 0 0 0]

 [0 0 0 0 0]

 [1 0 0 0 1]]

 

 

2. 지뢰가 있는 위치를 list로 만들기

list_i = []
list_j = []

for i in range(0, a.shape[0]):
    for j in range(0, a.shape[1]):
        if a[i][j] == 1:
            list_i.append(i)
            list_j.append(j)
print(list_i)
print(list_j)

Out[2] :

list_i = [0, 0, 1, 4, 4]

list_j = [0, 4, 1, 0, 4]

 

> 지뢰가 있는 위치가 (0, 0), (0, 4), (1, 1), (4, 0), (4, 4)이므로
   위의 위치를 (x, y)라 한다면

   x 의 위치는 list_i에 y의 위치는 list_j에 담았다.

  


3. 지뢰가 있는 곳의 좌,우,상,하,대각선에 1 넣기

for i, j in zip(list_i, list_j):
    a[max(i-1, 0):min(i+2, a.shape[0]),  max(j-1, 0):min(j+2, a.shape[1])] = 1

print(a)

Out[3] :

[[1 1 1 1 1]

 [1 1 1 1 1]

 [1 1 1 0 0]

 [1 1 0 1 1]

 [1 1 0 1 1]]

 

 

[ max, min를 쓴 이유 ]

>  만약 지뢰가 (1, 1)에 있다면 a[ i -1 : i +2,   j -1 : j +2 ] = 1을 이용해 1을 넣을 수 있다.

     i = 1,  j = 1 이므로  a[ 0 : 3 , 0 : 3 ] = 1 이다.

>  만약 지뢰가 (0, 0)에 있고 a[ i -1 : i +2,   j -1 : j +2 ] = 1을 이용한다면

    i = 0,  j = 0 이므로 a[ -1 : 2 , -1 : 2 ] = 1 이 된다.

    그러나 마이너스는 쓸 수 없으므로 최소값이 0이고, 최댓값이 행, 열의 길이가 될 것이다.

    따라서  a[max(i-1, 0):min(i+2, a.shape[0]),  max(j-1, 0):min(j+2, a.shape[1])] = 1

    으로 나타내어야 한다.

 

4. 1의 개수 세기

count = np.count_nonzero(a == 1)
print(count)

Out[4] :

21

 

5. 0의 개수 세기

a.shape[0]*a.shape[1] - count

Out[5] :

4

 


[다른 사람 풀이]

def solution(board):
    # 1. 행 or 열 길이
    n = len(board)
    
    # 2. 지뢰가 있는 위치 구하고 상,하,좌,우,대각선 위치도 찾기
    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])
    
    # 3. 안전지대 개수 구하기
    return n*n - sum(0 <= i < n and 0 <= j < n for i, j in danger)

[설명]

1. 행 or 열 길이

board = [[1, 0, 0, 0, 1], [0, 1, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [1, 0, 0, 0, 1]]
n = len(board)
print(n)

Out[6] :

5

 

> 행과 열이 5이므로 5가 나왔다.

 

 

2. 지뢰가 있는 위치 구하고 상, 하, 좌, 우, 대각선 위치도 찾기

danger = set()
for i, row in enumerate(board):
    for j, x in enumerate(row):
        if not x:
            continue
        print([(i, j) ])
        print([(i+di, j+dj) for di in [-1,0,1] for dj in [-1, 0, 1]])
       
        danger.update((i+di, j+dj) for di in [-1,0,1] for dj in [-1, 0, 1])
        print(danger)
        print()

Out[7] :

[(0, 0)]     # ( i , j )  첫 번째 지뢰의 위치 

[(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1)]      # ( 0, 0) 을 기준으로 상, 하, 좌, 우, 대각선 위치 

{(0, 1), (-1, -1), (0, 0), (-1, 1), (1, 1), (1, -1), (-1, 0), (1, 0), (0, -1)}     # 위의 값을 set으로 바꿔서 중복값 제거

 

[(0, 4)]      # ( i , j )   두 번째 지뢰의 위치 

[(-1, 3), (-1, 4), (-1, 5), (0, 3), (0, 4), (0, 5), (1, 3), (1, 4), (1, 5)]      # ( 0, 4) 을 기준으로 상, 하, 좌, 우, 대각선 위치 

{(0, 1), (1, 3), (0, 4), (-1, -1), (-1, 4), (0, 0), (1, 5), (-1, 1), (1, 1), (0, 3), (1, -1), (1, 4), (-1, 0), (-1, 3), (0, 5), (1, 0), (0, -1), (-1, 5)}    #  첫 번째 list, 두 번째  list를 set으로 바꿔서 중복값 제거 

 

[(1, 1)]     # ( i , j )   세 번째 지뢰의 위치 

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

{(0, 2), (0, 5), (2, 2), (1, 0), (1, 3), (-1, -1), (-1, 4), (-1, 1), (0, -1), (0, 1), (1, 2), (0, 4), (2, 1), (1, 5), (-1, 0), (-1, 3), (0, 0), (1, 1), (0, 3), (2, 0), (1, -1), (1, 4), (-1, 5)}

 

[(4, 0)]       # ( i , j )   네 번째 지뢰의 위치 

[(3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5), (5, 3), (5, 4), (5, 5)]

{(4, 0), (3, -1), (3, 4), (4, 3), (3, 1), (5, -1), (5, 4), (5, 1), (0, 2), (0, 5), (2, 2), (1, 0), (1, 3), (-1, -1), (-1, 4), (-1, 1), (3, 0), (4, 5), (3, 3), (5, 0), (5, 3), (0, -1), (0, 1), (1, 2), (0, 4), (2, 1), (1, 5), (-1, 0), (-1, 3), (4, -1), (4, 1), (3, 5), (4, 4), (5, 5), (0, 0), (1, 1), (0, 3), (2, 0), (1, -1), (1, 4), (-1, 5)}

 

 

[ enumerate 특징 ]

  • enumerate : 열거하다
  • 리스트가 있는 경우 순서와 리스트의 값을 전달
  • 이 함수는 순서가 있는 자료형(list, set, tuple, dictionary, string)을 입력으로 받아
    인덱스 값을 포함하는 enumerate 객체를 리턴함
  • 보통 enumerate 함수는 for문과 함께 자주 사용 됨
for i, row in enumerate(board):
  print()
  print(i, ':', row)
  for j, x in enumerate(row):
    print(j, ':', x)

Out[8] :

0 : [1, 0, 0, 0, 1]
0 : 1
1 : 0
2 : 0
3 : 0
4 : 1

1 : [0, 1, 0, 0, 0]
0 : 0
1 : 1
2 : 0
3 : 0
4 : 0

2 : [0, 0, 0, 0, 0]
0 : 0
1 : 0
2 : 0
3 : 0
4 : 0

3 : [0, 0, 0, 0, 0]
0 : 0
1 : 0
2 : 0
3 : 0
4 : 0

4 : [1, 0, 0, 0, 1]
0 : 1
1 : 0
2 : 0
3 : 0
4 : 1

 

3. 안전지대 개수 구하기

n*n - sum(0 <= i < n and 0 <= j < n for i, j in danger)

Out[9] :

4

 

> (전체 원소의 개수 (행 * 열)) -  (2번에서 찾은 위험지대의 개수)

    = 안전지대의 개수