
유기농 배추
문제 링크
백준 1012: 유기농 배추
문제 분석
- 배추밭에서 연결된 배추 그룹의 개수를 구하는 문제
- 각 연결된 그룹마다 지렁이 1마리씩 필요
- 상하좌우로 인접한 배추들은 같은 그룹
=>
**Connected Components 개수 구하기 문제!
-
각 배추에서 시작해서 연결된 모든 배추를 탐색하면 되는 문제로, 이 때 주의할 점은 한 번 방문한 배추는 다시 방문하지 않아야 함.
-
이 문제는 DFS, BFS 모두 가능하나, 최단 거리가 아닌 ‘연결성만 확인하면 되는’ 문제로 DFS 가 적합함.
-
각 칸을 최대 한 번씩은 방문하므로, O(M x N)의 시간복잡도를 가짐. (배추가 k개라고 해도, 전체 격자를 확인해야 함.)
-
최대 50x50 이므로 스택 오버플로우 위험 낮음
배운 점
코딩테스트 문제를 풀 때는 최대한 전역변수를 사용해서, 간단하게 풀려고 했으나 Swift로 풀면
38 |
39 | func dfs(_ y: Int, _ x: Int) {
| `- note: add '@MainActor' to make global function 'dfs' part of global actor 'MainActor'
40 | visited[y][x] = 1
위와 같은 문제가 빈번하게 발생함.
처음 코드
import Foundation
// connected components 문제!
var t = Int(readLine()!)!
var arr: [[Int]] = []
var visited: [[Int]] = []
var cnt = 0
let dy = [-1, 0, 1, 0]
let dx = [0, 1, 0, -1]
var (n, m, k) = (0, 0, 0)
while t > 0 {
// 가로: n, 세로: m, 배추 위치 개수: k
let input = readLine()!.components(separatedBy: " ").map { Int($0)! }
(n, m, k) = (input[0], input[1], input[2])
arr = Array(repeating: Array(repeating: 0, count: m), count: n)
visited = Array(repeating: Array(repeating: 0, count: m), count: n)
for i in 0..<k {
let s = readLine()!.components(separatedBy: " ").map { Int($0)! }
arr[s[0]][s[1]] = 1 // 배추 위치 입력
}
for i in 0..<n {
for j in 0..<m {
// 아직 방문하지 않았고, 배추가 심어져있는 곳이라면 dfs
if visited[i][j] == 0 && arr[i][j] == 1 {
dfs(i, j, &arr, &visited)
cnt += 1
}
}
}
t -= 1
}
func dfs( y: Int, x: Int, arr: [[Int]], visited: inout [[Int]]) {
visited[y][x] = 1
for i in 0..<4 {
let ny = y + dy[i]
let nx = x + dx[i]
if ny < 0 || ny >= n || nx < 0 || nx >= m { continue }
if visited[ny][nx] == 0 && arr[ny][nx] == 1 {
dfs(ny, nx)
}
}
}
print(cnt)
cnt 초기화를 누락, 매개변수를 누락하거나, print 위치를 잘못설정하는 작은 실수들이 있었음.
리팩토링 코드
import Foundation
// connected components 문제!
// 전역 변수
let t = Int(readLine()!)!
let dy = [-1, 0, 1, 0]
let dx = [0, 1, 0, -1]
func dfs(_ y: Int, _ x: Int, arr: [[Int]], visited: inout [[Int]], _ n: Int, _ m: Int) {
visited[y][x] = 1
for i in 0..<4 {
let ny = y + dy[i]
let nx = x + dx[i]
if ny < 0 || ny >= n || nx < 0 || nx >= m { continue }
if visited[ny][nx] == 0 && arr[ny][nx] == 1 {
dfs(ny, nx, arr, &visited, n, m)
}
}
}
// arr, n, m, visited 는 테스트 케이스마다 초기화를 시켜야 하는 것에 유의해야 함.
for _ in 0..<t {
let input = readLine()!.components(separatedBy: " ").map { Int($0)! }
let (n, m, k) = (input[0], input[1], input[2])
var arr = Array(repeating: Array(repeating: 0, count: m), count: n)
var visited = Array(repeating: Array(repeating: 0, count: m), count: n)
var cnt = 0 // 각 테스트케이스마다 초기화
for _ in 0..<k {
let pos = readLine()!.components(separatedBy: " ").map { Int($0)! }
arr[pos[0]][pos[1]] = 1
}
for i in 0..<n {
for j in 0..<m {
if visited[i][j] == 0 && arr[i][j] == 1 {
dfs(i, j, arr, &visited, n, m)
cnt += 1
}
}
}
print(cnt)
}정리
arr, n, m, visited 등 테스트 케이스마다 초기화를 시켜야 하는 것에 유의해야 함.
inout를 사용하여 함수 내에서 원본 변수를 직접 수정.&연산자를 사용하여 이 변수의 메모리 주소를 함수에 전달함. 함수가 원본에 직접 접근할 수 있게 해줌.