병아리는삐약삐약 2023. 8. 31. 16:19

문제 

- 크기가 N X M인 연구소
- 0 : 공간, 1 : 벽, 2 : 바이러스
- 바이러스는 상하좌우의 칸으로 퍼져나갈 수 있음
- 벽을 3개 세워서 바이러스를 막아야 함
- 바이러스가 퍼질 수 없는 안전 영역 크기의 최댓값을 구하라

 

입출력 예시

7 7
2 0 0 0 1 1 0
0 0 1 0 1 2 0
0 1 1 0 1 0 0
0 1 0 0 0 0 0 
0 0 0 0 0 1 1
0 1 0 0 0 0 0 
0 1 0 0 0 0 0 
27

 

정답

# ============= [0] ================
n,m = map(int,input().split())
data = []
temp = [[0] * m for _ in range(n)]

for _ in range(n):
    data.append(list(map(int,input().split())))

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

result = 0

# ============= [1] ================
def virus(x,y):
    for i in range(4):
        nx = x+dx[i]
        ny = y+dy[i]
         
        if 0 <= nx < n and 0 <= ny < m:
            if temp[nx][ny] == 0:
                temp[nx][ny] = 2
                virus(nx,ny)

# ============= [2] ================
def get_score():
    score = 0
    for i in range(n):
        for j in range(m):
            if temp[i][j] ==0:
                score+=1
    return score


# ============= [3-1] ================
def dfs(count):
    global result
    if count == 3:
        for i in range(n):
            for j in range(m):
                temp[i][j] = data[i][j]

        for i in range(n):
            for j in range(m):
                if temp[i][j]==2:
                    virus(i,j)

        result = max(result, get_score())
        return
        
# ============= [3-2] ================
    for i in range(n):
        for j in range(m):
            if data[i][j] == 0:
                data[i][j] = 1
                count += 1
                dfs(count)
                data[i][j] = 0
                count -= 1

# ============= [4] ================
dfs(0)
print(result)

 

풀이

이 문제는 크게 세 가지 함수를 만들어 풀 수 있는 문제였다. (못풀어서 답지봄..)

1. 3개의 벽을 세우는 모든 경우의 수를 구해
2. 각각의 경우마다 바이러스를 퍼뜨리고 
3. 그때의 안전구역의 크기를 구해서 이전의 안전구역과 비교한 후 더 큰 것을 갱신 

 

나는 파이썬 문법에 익숙하지 않아서 구현할 때 많이 힘들었다. 

그리고 경우의 수를 구할 때 파이썬 내장 모듈 itertools의 combination이라는 것도 알게 되었지만

사용하지 않고도 dfs로 풀 수 있는 문제였다. 

 

# ============= [0] ================

n,m = map(int,input().split())
data = []
temp = [[0] * m for _ in range(n)]

for _ in range(n):
    data.append(list(map(int,input().split())))

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

result = 0

 

여기서 data는 초기 입력받은 맵의 정보를 저장하는 리스트형 변수이고, 

temp는 각각의 경우의 수를 돌 때마다 data의 정보를 새로 받아서 맵을 초기화시키는 역할을 하는 변수이다. 

 

처음에 맵을 돌면서 0이면 2로 바꿔서 바이러스를 퍼뜨리거나, 0을 1로 바꿔 벽을 세워야 한다는 생각은 했었는데, 

그러면 다음 경우의 수를 돌 때는 수정된 맵을 탐색하게 되어버려서 난감했다. 

결론적으로는 처음 입력받은 맵정보를 저장할 리스트와,

그걸 매번 새로 갖다 쓸 리스트가 두 개 있어야 한다는 점이 포인트였다. 

 

 

# ============= [1] ================

def virus(x,y):
    for i in range(4):
        nx = x+dx[i]
        ny = y+dy[i]
         
        if 0 <= nx < n and 0 <= ny < m:
            if temp[nx][ny] == 0:
                temp[nx][ny] = 2
                virus(nx,ny)

 

각 경우의 수마다 바이러스를 퍼뜨리는 함수. 

처음 바이러스가 존재하는 좌표 (x,y)를 받아서

상하좌우 네 방향을 dfs로 탐색해나가면서 연결된 모든 0들을 2로 바꾼다.(바이러스를 퍼뜨리는 것을 의미)

 

 

 

# ============= [2] ================

def get_score():
    score = 0
    for i in range(n):
        for j in range(m):
            if temp[i][j] ==0:
                score+=1
    return score

 

안전구역의 크기를 구하는 함수. 

바이러스를 퍼뜨리고 나서 이 함수를 호출하여 각 경우의 수마다 안전구역의 크기를 계산할 수 있다

 

 

 

 

# ============= [3-1] ================

def dfs(count):
    global result
    if count == 3:
        for i in range(n):
            for j in range(m):
                temp[i][j] = data[i][j]

        for i in range(n):
            for j in range(m):
                if temp[i][j]==2:
                    virus(i,j)

        result = max(result, get_score())
        return
global
파이썬에서 사용하는 global 변수. 
전역변수를 함수 내에서 사용하고 싶을 때에는
global을 앞에 붙여줘야 전역변수로 선언된 result를 쓰는거구나 하고 알 수 있고, 
만약 붙여주지 않고 그냥 result라고 써버린다면 
전역으로선언된 result와 상관없는 새로운 지역변수 result가 생성되는 것임

 

count는 세워진 벽의 개수를 의미. 

벽이 3개 세워져있다면 초기의 맵정보인 data를 복사해와서  바이러스가 있는 좌표(x,y) 를 virus() 함수에 넘겨줌

virus함수는  === [1] === 파트에서 언급했지만 

상하좌우 네 방향을 dfs로 탐색해나가면서 연결된 모든 0들을 2로 바꾸는 작업을 통해 바이러스를 퍼뜨림

바이러스가 퍼졌으면 나머지 안전구역을 get_score() 함수로 구하고

최대값을 갱신하기 위해 이전의 result값과 비교해서 큰 값을 result에 새로 저장하게 됨

(이 때, 처음 result는 0으로 선언 및 초기화를 해 놨음)

 

 

 

# ============= [3-2] ================

 for i in range(n):
        for j in range(m):
            if data[i][j] == 0:
                data[i][j] = 1
                count += 1
                dfs(count)
                data[i][j] = 0
                count -= 1

 

벽을 3개 생성하는 로직. 

data[i][j] == 0 이면 1로 바꾸고 (벽생성)

dfs(count)를 재귀호출함으로써 count(벽 개수) == 3이 될때까지 0을 3개 찾아 1로 바꾸는 작업을 진행.

 

그리고 여기서 중요한 점은 마지막 2줄 

data[i][j] = 0
count == 1

 

에서 

data 변수는 초기의 맵 정보를 담고 있는 변수이기 때문에 dfs를 재귀호출 한 후

다시 바꿔줬던 값을 0으로 되돌리는 작업을 수행해야 함

(내가 해석한 게 정확한지는 모르겠지만 난 이런 이유라고 생각했음)

 

 

 

 

 

# ============= [4] ================

dfs(0)
print(result)

그리고 처음 벽의 개수인 0을 매개변수로 dfs 함수를 호출하면

자동으로 전역변수인 result값이 갱신될 것임

 

 

 

 

참조

- 이것이 취업을 위한 코딩테스트다. with 파이썬