Uknow's Lab.
article thumbnail

https://www.acmicpc.net/problem/7569

 

7569번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N과 쌓아올려지는 상자의 수를 나타내는 H가 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M ≤ 100, 2 ≤ N ≤ 100,

www.acmicpc.net

 

난이도 : 골드5
태그 : 그래프탐색, 너비우선탐색, 그래프이론

 

 

1. 설명

이전의 포스팅했던 백준 7576번 토마토는 한 층인 평면(x, y축)으로만 이루어졌던과 달리,

7569번 토마토는 복수의 층으로 입체(x,y,z)로 이루어져 있는게 차이점입니다.

 

이전 포스팅의 코드를 베이스로 약간 변형을 주어 3차원 BFS로 풀이할 수 있으며,

이전 포스팅의 내용이 궁금하신 분은 아래 링크를 참고해주세요.

 

2022.06.22 - [코딩테스트 & 알고리즘/Kotlin] - [백준 7576번] [Kotlin] 토마토

 

[백준 7576번] [Kotlin] 토마토

https://www.acmicpc.net/problem/7576 7576번: 토마토 첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다...

uknowblog.tistory.com

 

 

 

2. 소스코드

2.1. 좌표를 담을 데이터 클래스와 방향 선언

<kotlin />
data class Node(val x: Int, val y: Int, val z: Int) val dx = arrayOf(0, 0, 1, -1, 0, 0) val dy = arrayOf(1, -1, 0, 0, 0, 0) val dz = arrayOf(0, 0, 0, 0, 1, -1)

 

각 좌표를 담을 Node 클래스를 선언합니다. 기존 x, y층에 이어 z축(층) 까지 포함되어 있습니다.

 

방향을 도와줄 dx, dy에 이어 dz도 선언합니다.

 

2.2. 인풋값 저장

<code />
val br = BufferedReader(InputStreamReader(System.`in`)) val mn = br.readLine()!!.split(" ") // m, n 순으로 입력받음 val n = mn[1].toInt() val m = mn[0].toInt() val h = mn[2].toInt() var day = 0 val map = Array(n) { Array(m) { Array(h) { 0 } } } val startNodes: Queue<Node> = LinkedList() repeat(h) { z -> repeat(n) { x -> val st = StringTokenizer(br.readLine(), " ") repeat(m) { y -> map[x][y][z] = st.nextToken().toInt() if (map[x][y][z] == 1) { startNodes.offer(Node(x, y, z)) } } } }

bufferedReader와 StringTokenizer를 이용하여 인풋값들을 저장합니다.

단, 토마토가 있을 경우 startNode에 저장해줍니다.

 

 

2.3. BFS

<kotlin />
val q: Queue<Node> = LinkedList() startNodes.forEach { q.offer(it) } q.offer(Node(-1, -1, -1)) while (q.isNotEmpty()) { val target = q.poll() if (target.x == -1 && target.y == -1 && target.z == -1) { day++ if (q.isNotEmpty()) { q.offer(Node(-1, -1, -1)) } } for (i in 0 until 6) { val nx = target.x + dx[i] val ny = target.y + dy[i] val nz = target.z + dz[i] if (nx !in 0 until n || ny !in 0 until m || nz !in 0 until h || map[nx][ny][nz] == 1 || map[nx][ny][nz] == -1) continue map[nx][ny][nz] = 1 q.offer(Node(nx, ny, nz)) } }

이제 3차원 맵을 대상으로 BFS를 하면 됩니다.

 

하나씩 보겠습니다.

 

 

<code />
val q: Queue<Node> = LinkedList() startNodes.forEach { q.offer(it) } q.offer(Node(-1, -1, -1))

토마토가 있는 곳을 시작으로 BFS를 실시하여야 하니,

토마토의 위치를 저장했던 startNode의 값들을 빼내어 큐에 넣습니다.

 

이후 한 사이클이 돌았다는 것을 표시하기 위해 -1,-1,-1 이라는 좌표를 넣어줍니다.

 

 

 

<code />
val target = q.poll() if (target.x == -1 && target.y == -1 && target.z == -1) { day++ if (q.isNotEmpty()) { q.offer(Node(-1, -1, -1)) } }

다음은 while문 안쪽으로 들어갑니다.

 

큐에서 값을 하나 빼와 한 사이클이 돌았다는 것을 의미하는 (-1,-1,-1)좌표가 나오면

날짜값을 담고있는 day 변수를 +1만큼 늘려줍니다.

이후, 한 사이클이 돌았다는 것은 곧 지금 현재 한 사이클만의 원소가 큐에 담겨있다는 의미이므로,

다시 큐에 (-1,-1,-1)이라는 좌표를 넣습니다.

 

<kotlin />
for (i in 0 until 6) { val nx = target.x + dx[i] val ny = target.y + dy[i] val nz = target.z + dz[i] if (nx !in 0 until n || ny !in 0 until m || nz !in 0 until h || map[nx][ny][nz] == 1 || map[nx][ny][nz] == -1) continue map[nx][ny][nz] = 1 q.offer(Node(nx, ny, nz)) }

이후, 상-하, 동-서-남-북 방향으로 해당 좌표가 방문하지 않은 곳이라면 방문처리를 해주고 큐에 넣습니다.

 

 

2.4. 출력

<code />
map.forEach { it -> it.forEach { it2 -> it2.forEach { it3 -> if (it3 == 0) { println(-1) return } } } } println(day - 1)

만약, 토마토가 익지 않은 지점(0)이 있다면 -1을 출력하고, main 함수를 종료합니다.

 

그렇지 않다면 토마토가 익는데 걸린 시간을 출력합니다.

 

 

2.5. 전체 소스코드

<kotlin />
import java.io.BufferedReader import java.io.InputStreamReader import java.util.LinkedList import java.util.Queue import java.util.StringTokenizer data class Node(val x: Int, val y: Int, val z: Int) val dx = arrayOf(0, 0, 1, -1, 0, 0) val dy = arrayOf(1, -1, 0, 0, 0, 0) val dz = arrayOf(0, 0, 0, 0, 1, -1) fun main() { val br = BufferedReader(InputStreamReader(System.`in`)) val mn = br.readLine()!!.split(" ") // m, n 순으로 입력받음 val n = mn[1].toInt() val m = mn[0].toInt() val h = mn[2].toInt() var day = 0 val map = Array(n) { Array(m) { Array(h) { 0 } } } val startNodes: Queue<Node> = LinkedList() repeat(h) { z -> repeat(n) { x -> val st = StringTokenizer(br.readLine(), " ") repeat(m) { y -> map[x][y][z] = st.nextToken().toInt() if (map[x][y][z] == 1) { startNodes.offer(Node(x, y, z)) } } } } val q: Queue<Node> = LinkedList() startNodes.forEach { q.offer(it) } q.offer(Node(-1, -1, -1)) while (q.isNotEmpty()) { val target = q.poll() if (target.x == -1 && target.y == -1 && target.z == -1) { day++ if (q.isNotEmpty()) { q.offer(Node(-1, -1, -1)) } } for (i in 0 until 6) { val nx = target.x + dx[i] val ny = target.y + dy[i] val nz = target.z + dz[i] if (nx !in 0 until n || ny !in 0 until m || nz !in 0 until h || map[nx][ny][nz] == 1 || map[nx][ny][nz] == -1) continue map[nx][ny][nz] = 1 q.offer(Node(nx, ny, nz)) } } map.forEach { it -> it.forEach { it2 -> it2.forEach { it3 -> if (it3 == 0) { println(-1) return } } } } println(day - 1) }

 

 

3. 후기

분명 풀었던 문제였는데, 풀지 않았다 되있어서 의아했더니

 

알고보니 기존 문제는 2차원 BFS, 현재 문제는 3차원 BFS로 다른 문제였습니다.

 

기존에 작성한 코드가 있으니, 간단한 수정만 거쳐 그리 어렵지 않게 풀었던 것 같습니다.

profile

Uknow's Lab.

@유노 Uknow

인생은 Byte와 Double 사이 Char다. 아무말이나 해봤습니다.