BOJ[12886] - 돌 그룹 by JavaScript
돌 그룹
문제
언어
- JavaScript
순서도
- BFS 탐색
문제 풀이 step 1
- 오늘 강호는 돌을 이용해 재미있는 게임을 하려고 합니다.
- 돌은 세 개의 그룹으로 나누어져 있으며 각각의 그룹에는 돌이 A, B, C 개가 있습니다.
- 강호는 모든 그룹에 있는 돌의 개수를 같게 만들려고 합니다.
- 강호는 돌을 단계별로 움직이며, 각 단계는 다음과 같이 이루어져 있습니다.
- 크기가 같지 않은 두 그룹을 고릅니다.
- 그 다음, 돌의 개수가 작은 쪽을 X, 큰 쪽을 Y 라고 정합니다.
- 그 다음, X 에 있는 돌의 개수를 X + X 개로, Y 에 있는 돌의 개수를 Y - X 개로 만듭니다.
- A, B, C 가 주어졌을 때, 강호가 돌을 같은 개수로 만들 수 있으면 1 을, 아니면 0 을 출력하는 문제입니다.
문제 풀이 step 2
- 3 차원 공간에 시작점 (a, b, c) 를 하나의 노드로 생각합니다.
- 그리고 돌을 움직이는 규칙에 따라서 BFS 탐색을 합니다.
- 탐색을 할 때 방문 처리를 해야하는데, 3 차원 배열을 생성하면 메모리 초과가 발생합니다.
- 따라서 Map 을 이용해서 방문 처리를 하고, (a, b, c) 를 적절히 문자열로 변환 후 key 값으로 사용합니다.
- BFS 탐색을 모두 마치면 세 개의 돌이 같아지는 순간이 있는 지 검사하고, 있다면 1 을, 없다면 0 을 출력합니다.
- 추가 설명은 주석으로 작성하겠습니다.
후기
- 꼭 다시 풀어볼 문제입니다.
- 처음에는 무지성으로 문제를 풀었더니 메모리와 소요시간이 너무 크게 발생했습니다.
- 그래서 분석하고 공부 후 다시 풀어봤습니다.
- 문제를 풀 때, 무지성을 지양하고, 문제를 수학적으로 분석하고 풀어야 할 것 같습니다.
소스 코드
const input = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
class Queue {
constructor() {
this.bucket = [];
this.front = -1;
this.rear = -1;
}
enqueue(item) {
this.bucket[++this.rear] = item;
}
dequeue() {
return this.bucket[++this.front];
}
isEmpty() {
return this.front === this.rear;
}
}
const bfs = (a, b, c) => {
const queue = new Queue();
// Map 을 이용해서 방문처리
const visitedMap = new Map();
if (a === b && b === c) return true;
queue.enqueue([a, b, c]);
visitedMap.set([a, b, c].join(" "), true);
while (!queue.isEmpty()) {
const [x, y, z] = queue.dequeue();
const next = [];
// 규칙에 맞게 다음 탐색 노드 결정
if (x !== y) {
if (x < y) {
next.push([x + x, y - x, z]);
} else {
next.push([x - y, y + y, z]);
}
}
if (x !== z) {
if (x < z) {
next.push([x + x, y, z - x]);
} else {
next.push([x - z, y, z + z]);
}
}
if (y !== z) {
if (y < z) {
next.push([x, y + y, z - y]);
} else {
next.push([x, y - z, z + z]);
}
}
for (let i = 0; i < next.length; i++) {
const [nx, ny, nz] = next[i];
const key = next[i].join(" ");
// 이미 방문 했다면 SKIP
if (visitedMap.get(key) === true) continue;
if (nx === ny && ny === nz) return true;
// 방문 처리
visitedMap.set(key, true);
queue.enqueue([nx, ny, nz]);
}
}
return false;
};
const solution = (input) => {
const [a, b, c] = input[0].split(" ").map(Number);
const result = bfs(a, b, c);
if (result === true) return 1;
return 0;
};
console.log(solution(input));
다른 방식의 문제 풀이 step 1
- 중요한 포인트
- 3 개의 그룹의 돌의 개수의 합은 변하지 않습니다.
- 문제에서 주어진 단계를 보면 돌이 그룹 안에서만 이동을 하기 때문에 전체적인 돌의 개수는 변하지 않습니다.
- 개선점
- 3 개의 수가 같은 수가 되려면, 이 수들의 합이 3 의 배수여야 합니다.
- 너무 당연한 건데 문제를 풀 때는 본 내용이 잘 보이진 않습니다.
- 따라서, 초기에 각 그룹에 있는 돌의 개수를 모두 더한 값이 3 의 배수인지 확인해줍니다.
- 각 돌 그룹을 구분할 필요가 없습니다.
- 따라서, 매 단계마다 돌을 오름차순으로 정렬 후에 로직을 전개해도 됩니다.
- 정렬을 하게 되면, 돌의 개수가 작은 쪽과 큰 쪽을 구분하기 쉬워집니다.
- 방문처리를 2 차원 배열로 해도 무방합니다.
- 오름차순으로 정렬하게 되면 가장 작은 돌의 개수와 그 다음으로 큰 돌의 개수만 방문처리를 해도 됩니다.
- 왜냐하면, 전체적인 돌의 개수가 고정되어 있기에 두 개로 나머지 하나를 결정할 수 있기 때문입니다.
- 3 개의 수가 같은 수가 되려면, 이 수들의 합이 3 의 배수여야 합니다.
- 이런 개선점을 적용한 소스 코드를 아래에 작성합니다.
다른 방식의 소스 코드
const input = require("fs")
.readFileSync("/dev/stdin")
.toString()
.trim()
.split("\n");
class Queue {
constructor() {
this.bucket = [];
this.front = -1;
this.rear = -1;
}
enqueue(item) {
this.bucket[++this.rear] = item;
}
dequeue() {
return this.bucket[++this.front];
}
isEmpty() {
return this.front === this.rear;
}
}
const bfs = (a, b, c) => {
const queue = new Queue();
// 2 차원 배열로 방문처리
const visited = Array.from({length: 1501}, () => Array(1501).fill(false));
queue.enqueue([a, b, c]);
visited[a][b] = true;
while (!queue.isEmpty()) {
const [x, y, z] = queue.dequeue();
const next = [];
// 정렬했으니 대소 비교는 이미 되어있는 상태
if (x !== y) next.push([x + x, y - x, z].sort((a, b) => a - b));
if (x !== z) next.push([x + x, y, z - x].sort((a, b) => a - b));
if (y !== z) next.push([x, y + y, z - y].sort((a, b) => a - b));
for (let i = 0; i < next.length; i++) {
const [nx, ny, nz] = next[i];
// 2 차원으로 방문처리
if (visited[nx][ny] === true) continue;
if (nx === ny && ny === nz) return 1;
visited[nx][ny] = true;
queue.enqueue([nx, ny, nz]);
}
}
return 0;
};
const solution = (input) => {
const [a, b, c] = input[0]
.split(" ")
.map(Number)
.sort((a, b) => a - b);
// 각 그룹의 돌의 합은 변하지 않으므로, 3 의 배수인지 검사
if ((a + b + c) % 3 !== 0) return 0;
if (a === b && b === c) return 1;
const result = bfs(a, b, c);
return result;
};
console.log(solution(input));