백준 1012번: 유기농 배추
https://www.acmicpc.net/problem/1012
1012번: 유기농 배추
차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에
www.acmicpc.net
설명
차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추 근처에 서식하며 해충을 잡아먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.
한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다. 0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 |
입력
입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로 길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그다음 K 줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.
출력
각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.
풀이 과정
BFS 사용
# 큐를 쓰기 위해 collections 모듈 가져오기
# list 를 안 쓰고 deque를 쓰는 이유는 list의 pop은 O(n) 이지만 deque는 O(1) 의 복잡도를 가짐.
from collections import deque
def BFS(graph, 탐색_x, 탐색_y):
queue = deque() # 큐 선언
queue.append( (탐색_x, 탐색_y) ) # 큐에 최초 그래프 시작점(최초 배추 만난 좌표) 삽입
graph[탐색_y][탐색_x] = 0 # 탐색한 그래프는 0으로 변경하여 재탐색 막음
# for 문으로 상하좌우를 탐색하기 위해 단위 좌표 선언
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
# 큐에 값이 있는 동안 BFS 계속 시행
while queue:
x, y = queue.popleft() # enqueue 해서 값 가져오기
# 좌표 범위를 넘지 않는 상하좌우에 대해 1인 값(배추가 있는 곳)을 찾으면 큐에 넣어 계속 탐색
for i in range(4):
인접_x = x + dx[i]
인접_y = y + dy[i]
# 좌표 범위를 넘지 않고 값이 1인 (배추가 있는) 좌표값에 대해 재탐색을 막고 큐에 넣음
if ( 0 <= 인접_x < M and 0 <= 인접_y < N and graph[인접_y][인접_x] == 1 ):
graph[인접_y][인접_x] = 0
queue.append( (인접_x, 인접_y) )
return # 더이상 큐에 탐색할 값이 없으면 함수 종료
T = int(input()) # 테스트 케이스 입력
for _ in range(T):
cnt = 0 # BFS 실행한 횟수 (=필요한 지렁이 개수) 기록용 변수 선언
M, N, K = map(int, input().split()) # 가로길이, 세로길이, 배추개수 입력
graph = [ [0] * M for _ in range(N) ] # 가로 M 세로 N의 2차원 배열 선언
# 배추 위치 입력 파트
for _ in range(K):
배추_x, 배추_y = map(int, input().split()) # 배추 좌표를 입력받아서
graph[배추_y][배추_x] = 1 # 좌표에 배추를 1로 표시 ( 배열의 y가 먼저 오는 것에 주의)
# 배추 묶음 (지렁이 개수) 체크 파트
for 탐색_y in range(N): # N 은 세로길이 이므로 y 값
for 탐색_x in range(M): # M 은 가로길이 이므로 x 값
if graph[탐색_y][탐색_x] == 1: # 첫 배추 좌표를 확인하면
BFS(graph, 탐색_x, 탐색_y) # BFS 실행 후
cnt += 1 # 카운팅
print(cnt) # 카운팅한 배추 묶음 개수 출력
처음에는 인접행렬이나 인접 리스트로 배추밭을 구현하려고 했는데, 배추밭 속에서 다수의 그래프를 찾아야 하는 문제여서 배추밭을 2차원 배열로 구현한 뒤에 순차적으로 그래프를 찾아나가고 그래프를 발견하면 BFS/DFS를 쓰면 되겠다 싶었다.
근데 구현을 어떻게 할지 모르겠어서,, 구글링 해서 구현의 큰 흐름은 파악했으나, 사람들이 변수명을 너무 막 써서 이게 어떤 값인지 잘 모르겠어서.. 내가 머리 조금 써서 한글로 수정을 했다.
테스트 케이스 T를 먼저 입력받고, 각각의 T에 대한 for 문으로 M(가로길이), N(세로 길이), K(배추 개수)를 입력받아 각각 0의 값을 가지는 N * M의 2차원 배열(행렬)을 만들었고, 이후 추가로 입력받는 배추 X, Y 좌표값에 대해서는 배열의 값만 1로 바꾸어줬다.
그리고 이후로는 각 좌푯값을 돌면서 배추를 발견하면 (1을 만나면) 하나의 그래프를 발견했다고 보고 BFS를 실행하여 그 그래프의 값을 0으로 만든 후 그래프 개수를 카운팅 하였고, 마지막에 총 그래프 묶음을 출력해주었다.
여기서 deque 를 가져다 썼는데, 이유는 list로 쓰면 pop 메서드를 쓸 때 O(N)의 복잡도를 가지지만, deque는 O(1)의 복잡도를 가져서 이다.
DFS 사용
import sys
sys.setrecursionlimit(10000)
def DFS(graph, 탐색_x, 탐색_y):
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
for i in range(4):
인접_x = 탐색_x + dx[i]
인접_y = 탐색_y + dy[i]
if ( 0 <= 인접_x < M and 0 <= 인접_y < N and graph[인접_y][인접_x] == 1 ):
graph[인접_y][인접_x] = 0
DFS(graph, 인접_x, 인접_y)
return
T = int(input())
for _ in range(T):
cnt = 0
M, N, K = map(int, input().split())
graph = [ [0] * M for _ in range(N) ]
for _ in range(K):
배추_x, 배추_y = map(int, input().split())
graph[배추_y][배추_x] = 1
for 탐색_y in range(N):
for 탐색_x in range(M):
if graph[탐색_y][탐색_x] == 1:
DFS(graph, 탐색_x, 탐색_y)
cnt += 1
print(cnt)
BFS의 차이점으로 DFS는 재귀를 사용하였다. 여기서 sys.setrecursionlimit(10000) 이 코드는 재귀의 제한을 만 번으로 제한한다는 뜻인데 기본값이 1000으로 문제를 풀기에는 턱없이 적어서 한도를 늘려주는 코드를 작성하여야 했다.
큰 틀은 BFS 와 같고 재귀를 사용한다는 점만 차이가 있다.
[스터디 리뷰]
graph[y][x] 로 했는데, 일반적으로는 행렬로 m * n 으로 한다는 듯. 보통은 n m 으로 인풋을 주지만 이 문제에서는 다르게 줘서 내가 헷갈린듯?
그리고 또 BFS /DFS 쓸 때 보통은 탐색하면서 1->2->3... 처럼 숫자를 올려가는게 국룰이라고 한다! 다른 FS 문제도 다 풀어봐야지
'알고리즘 🤔' 카테고리의 다른 글
[백준 14888] 연산자 끼워넣기 (1) | 2022.10.04 |
---|---|
[백준 5427] 불 (0) | 2022.09.27 |
[백준 1941] 소문난 칠공주 (1) | 2022.09.20 |
[백준 6198] 옥상 정원 꾸미기 (0) | 2022.09.20 |
[백준 11729] 하노이 탑 이동 순서 (0) | 2022.09.13 |