문제
다들 문제보자마자 대충 이렇게 풀어야겠다 머릿속에 떠오르죠?
실버5의 구현 문제기도 해서 큰 난이도를 자랑하는 문제는 아니지만
"다른 사람은 어떻게 풀었지?" 라고 생각하는 사람도 생길거라 생각해 남겨봅니다
이 문제는 크게 두 가지 방법으로 풀 수 있는데요
풀이
풀이 1 - 지뢰를 기준으로 인접한 칸들의 지뢰 개수를 더한다
간단하게 설명하자면
- 처음 칸부터 시작하다 지뢰(정수)를 만나면
- 새로운 배열에서 지뢰가 있던 자리를 표시하고
- 주변 칸에 지뢰 수만큼 더해준다
- 그리고 10넘는 지뢰들은 "M" 으로 만들어준다
지뢰를 기준으로 주변의 가중치를 더해주는 방법입니다
더보기
// 입력부분
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const N = Number(input[0]);
const grid = input.slice(1).map(row => row.split(""));
const result = Array.from({ length: N }, () => Array(N).fill(0));
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1],
];
// 지뢰 기준으로 주변 8칸에 숫자 더하기
for (let x = 0; x < N; x++) {
for (let y = 0; y < N; y++) {
if (grid[x][y] !== ".") {
const value = Number(grid[x][y]);
result[x][y] = "*";
for (const [dx, dy] of directions) {
const nx = x + dx;
const ny = y + dy;
if (
nx >= 0 && nx < N &&
ny >= 0 && ny < N &&
result[nx][ny] !== "*"
) {
result[nx][ny] += value;
}
}
}
}
}
// 숫자 후처리
for (let x = 0; x < N; x++) {
for (let y = 0; y < N; y++) {
if (result[x][y] !== "*" && result[x][y] >= 10) {
result[x][y] = "M";
} else if (result[x][y] !== "*") {
result[x][y] = result[x][y].toString();
}
}
}
console.log(result.map(row => row.join("")).join("\n"));
풀이 2 - 칸을 기준으로 인접한 지뢰들의 개수를 더한다
이렇게 공백을 기준으로 풀 수도 있는데요
- 일단 "." 들을 전부 숫자 0, 지뢰들의 숫자들의 타입을 정수로 바꿔준다
- 처음 칸부터 시작하다 공백을 만나면 주변에 모든 지뢰를 더한다
- 그리고 10넘는 지뢰들은 "M" 으로 만들어준다
더보기
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");
const N = Number(input[0]);
const grid = input.slice(1).map(row =>
row.split("").map(c => (c === "." ? 0 : Number(c)))
);
const result = Array.from({ length: N }, () => Array(N).fill(0));
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1],
];
for (let x = 0; x < N; x++) {
for (let y = 0; y < N; y++) {
if (grid[x][y] > 0) {
result[x][y] = "*";
continue;
}
let sum = 0;
for (const [dx, dy] of directions) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < N && ny >= 0 && ny < N && grid[nx][ny] > 0) {
sum += grid[nx][ny];
}
}
result[x][y] = sum >= 10 ? "M" : sum.toString();
}
}
console.log(result.map(row => row.join("")).join("\n"));
비교
그래서 방법1, 방법2 중에 어떤 방법을 써야할까?
지뢰의 총 개수를 G 라고 할 때
방법 1의 연산 횟수는 G*8 입니다. 지뢰의 개수에 따라 연산 횟수가 달라집니다
방법 2의 연산 횟수는 (N × N) * 8입니다. 지뢰가 몇 개 있던 모든 칸을 다 검사하지만 연산 횟수는 고정적입니다
- 지뢰의 갯수가 많다면? (100 x 100 판의 20개의 지뢰 )
지뢰 기준 | 20 * 8 = 160 회 연산 | 적음 |
빈칸 기준 | 10000 * 8 = 80000 회 연산 | 많음 |
- 하지만 지뢰가 50%를 넘어간다면 ?
지뢰가 매우 적음 | G * 8 → 매우 작음 | ✅ 지뢰 기준 퍼뜨리기 |
지뢰가 거의 없음 | (N*N) * 8 → 비효율 | ❌ 빈칸 기준 불리함 |
지뢰가 많음 (50% 이상) | 지뢰 기준은 중복 많음 | ✅ 빈칸 기준이 오히려 빠름 |
이처럼 G 가 N * N 보다 커지는데 주의하면 어떤 방식이 유용한지 알 수 있습니다
결론
사실 연산 횟수가 크게 중요하지 않은 문제기도 하지만 (시간복잡도도 같음)
어느 한 가지 방법을 선택했을 때 왜 이 방식으로 풀었나 물어보면 어떻게 대답할지 막막해서 정리해봤습니다
- 지뢰가 적다 → 방법 1 (지뢰 기준)
- 전체 결과가 필요하다 → 방법 2 (탐색 방식)