문제
N×M의 모눈종이 위에 아주 얇은 치즈가 <그림 1>과 같이 표시되어 있다. 단, N 은 세로 격자의 수이고, M 은 가로 격자의 수이다. 이 치즈는 냉동 보관을 해야만 하는데 실내온도에 내어놓으면 공기와 접촉하여 천천히 녹는다. 그런데 이러한 모눈종이 모양의 치즈에서 각 치즈 격자(작 은 정사각형 모양)의 4변 중에서 적어도 2변 이상이 실내온도의 공기와 접촉한 것은 정확히 한시간만에 녹아 없어져 버린다. 따라서 아래 <그림 1> 모양과 같은 치즈(회색으로 표시된 부분)라면 C로 표시된 모든 치즈 격자는 한 시간 후에 사라진다.

<그림 2>와 같이 치즈 내부에 있는 공간은 치즈 외부 공기와 접촉하지 않는 것으로 가정한다. 그러므 로 이 공간에 접촉한 치즈 격자는 녹지 않고 C로 표시된 치즈 격자만 사라진다. 그러나 한 시간 후, 이 공간으로 외부공기가 유입되면 <그림 3>에서와 같이 C로 표시된 치즈 격자들이 사라지게 된다.


모눈종이의 맨 가장자리에는 치즈가 놓이지 않는 것으로 가정한다. 입력으로 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 구하는 프로그램을 작성하시오.
입력
첫째 줄에는 모눈종이의 크기를 나타내는 두 개의 정수 N, M (5 ≤ N, M ≤ 100)이 주어진다. 그 다음 N개의 줄에는 모눈종이 위의 격자에 치즈가 있는 부분은 1로 표시되고, 치즈가 없는 부분은 0으로 표시된다. 또한, 각 0과 1은 하나의 공백으로 분리되어 있다.
출력
출력으로는 주어진 치즈가 모두 녹아 없어지는데 걸리는 정확한 시간을 정수로 첫 줄에 출력한다.

정답코드
const fs = require("fs");
const input = fs.readFileSync("./input2.txt").toString().trim().split("\n");
class Queue {
constructor() {
this.items = {};
this.headIndex = 0;
this.tailIndex = 0;
}
enqueue(item) {
this.items[this.tailIndex] = item;
this.tailIndex++;
}
dequeue() {
const item = this.items[this.headIndex];
delete this.items[this.headIndex];
this.headIndex++;
return item;
}
peek() {
return this.items[this.headIndex];
}
size() {
return this.tailIndex - this.headIndex;
}
}
const removeCheese = () => {
let changed = false; // 하나라도 녹인 치즈가 있는지 없는지 확인
for (let i = 0; i < N; i++) {
for (let j = 0; j < M; j++) {
if (map[i][j] >= 3) {
map[i][j] = 0;
changed = true;
}
if (map[i][j] === 2) {
map[i][j] = 1;
}
}
}
return changed;
};
const [N, M] = input[0].split(" ").map(Number);
const map = input.splice(1).map((line) => line.split(" ").map(Number));
const dx = [-1, 1, 0, 0];
const dy = [0, 0, -1, 1];
let count = 0;
while (true) {
const visited = Array.from({ length: N }, () => Array(M).fill(false));
const queue = new Queue();
queue.enqueue([0, 0]);
visited[0][0] = true;
while (queue.size() !== 0) {
const [x, y] = queue.dequeue(); // 큐에 들어간 좌표 출력
for (let i = 0; i < 4; i++) {
const nx = x + dx[i];
const ny = y + dy[i];
if (nx >= N || nx < 0 || ny >= M || ny < 0 || visited[nx][ny]) continue;
if (map[nx][ny] >= 1) {
// 1이상이라는 것은 치즈임
map[nx][ny]++; // 4방향으로 갔을때 만났다면 1면이 닿았으니 1증가
} else {
queue.enqueue([nx, ny]);
visited[nx][ny] = true;
}
}
}
if (removeCheese()) {
count++;
} else {
console.log(count);
return;
}
}
이번 문제는 그냥 다른 곳에서 코드를 옮겨왔기 때문에 어떻게 해결했는지 스스로 코드를 보면서 공부하려고 기록한다.
우선 class Queue는 자바스크립트에는 내장된 queue가 따로 없기 때문에 queue까지 구현한 것 같다.
따로 구현하지 않아도 문제는 해결할 수 있다.
다른 bfs 문제와 다르지 않게 (0, 0) 지점부터 시작하여 탐색을 시작한다.
중요한 건 while 문안에 4방향을 탐색할 때의 조건인데 치즈는 1로 표시되어 있고 두 방향이 맞닿아야 사라진다고 했다. 따라서 숫자 1인 칸을 만났다면 이 칸은 한번 접촉됐다는 의미로 값만 증가시켜고 주고 실제 방문 표시와 queue에는 삽입하지 않는다. 왜냐하면 1인 치즈는 2번 이상 맞닿아야(칸의 값이 3 이상이 되어야) 사라 질수 있기 때문이다.
그다음 2번째 while 문이 종료되면 치즈를 녹이는 코드가 실행되는데 아래 코드다.
따로 가져온 removeCheese 함수의 코드를 보면 처음에는 모든 칸을 탐색하면서 칸의 값이 3 이상 이면, 즉 2번 이상 접촉이 되었다면 녹였을 경우가 발생했기 때문에 changed = false -> true로 바꿔주고 녹였기 때문에 0으로 바꿔준다.
그런데 만약 값이 2라면 즉 1번만 닿았다면 녹지 않았기 때문에 다시 2번 이상 닿아야 녹아야 하기 때문에 기존 값인 1로 바꿔준다.
이러한 과정을 반복하면서 removeCheese()가 false, 녹일 치즈가 없을 때까지 반복하면서 최종 결과 값을 도출한다.
if (removeCheese()) {
count++;
} else {
console.log(count);
return;
}
const removeCheese = () => {
let changed = false; // 하나라도 녹인 치즈가 있는지 없는지 확인
for (let i = 0; i < N; i++) {
for (let j = 0; j < M; j++) {
if (map[i][j] >= 3) {
map[i][j] = 0;
changed = true;
}
if (map[i][j] === 2) {
map[i][j] = 1;
}
}
}
return changed;
};