[백준] 1041 주사위 (Javascript)

잭슨·2024년 4월 5일
0

알고리즘 문제 풀이

목록 보기
99/130
post-thumbnail

문제

BOJ1041_주사위

풀이

이 문제는 직접 그림으로 그려보면 방법을 떠올리기가 쉽다.
N3N^3개의 주사위로 정육면체를 만들었다고 가정해보자.

이 정육면체를 구성한 주사위의 한 면만 보이는 부분은 아래와 같을 것이다.

그리고 주사위의 인접한 두 면만 보이는 부분은 아래와 같다.

마지막으로 주사위의 인접한 세 면이 보이는 부분은 아래와 같다.

이렇게 구성하면 정육 면체의 모든 부분이 채워진다.

해당 정육면체에서 빨간 영역은 주사위의 한 면만 보이는 부분이기 때문에 주사위의 가장 작은 눈금이 왔을 때 최솟값을 갖는다.

초록색 영역은 주사위의 인접한 두 면의 합이 최소인 조합을 선택하면 된다. 인접한 두 면이란 마주보지 않는 두 면을 말한다.

파란색 영역은 주사위의 인접한 세 면의 합이 최소인 조합을 선택하면 된다. 인접한 세 면이란 각각의 면이 서로 마주보지 않는 세 개의 면을 말한다.

예를 들어 주사위의 눈금이 1 2 3 4 5 6 일 경우,

우선 주사위의 두 면의 합과, 세 면의 합을 구할 때 각각의 면들이 서로 마주보고 있는지 아닌지 확인해 주기 위해 마주보는 면의 인덱스를 저장하는 배열을 만들어야 한다.

const across = [5, 4, 3, 2, 1, 0];

그리고 최솟값을 구해주자

한 면만 보이는 부분 = 1

let min1 = Math.min(...dice);

두 면이 보이는 부분 = 1+2

let min2 = Infinity;

for (let i = 0; i < 6; i++) {
    for (let j = i + 1; j < 6; j++) {
        if (j === across[i]) continue; // 두 면이 맞은 편이라면 스킵
        min2 = Math.min(min2, dice[i] + dice[j]);
    }
}

세 면이 보이는 부분 = 1+2+3

let min3 = Infinity;
for (let i = 0; i < 6; i++) {
    for (let j = i + 1; j < 6; j++) {
        if (j === across[i]) continue;
        for (let k = j + 1; k < 6; k++) {
            if (k === across[i] || k === across[j]) continue; // 세 면 중 하나라도 맞은 편이라면 스킵
            min3 = Math.min(min3, dice[i] + dice[j] + dice[k]);
        }
    }
}

그리고 이렇게 구한 값을 주사위의 개수에 맞게 더해준다.

let sum1 = ((N - 2) * (N - 1) * 4 + (N - 2) ** 2) * min1;
let sum2 = ((N - 1) * 4 + (N - 2) * 4) * min2;
let sum3 = min3 * 4;

또한 N=1일 경우(주사위가 한 개인 경우)에는 위의 공식이 성립하지 않기 때문에, 모든 눈금을 합하고 그 중 최댓값을 빼서, 최소합을 만들어주면 된다.

N === 1 ? dice.reduce((acc, cur) => (acc += cur), 0) - Math.max(...dice)

완성된 코드는 아래와 같다.

코드

const filePath = process.platform === 'linux' ? '/dev/stdin' : './Javascript/input.txt';
const input = require('fs').readFileSync(filePath).toString().trim().split('\n');
const N = +input[0];
const dice = input[1].split(' ').map(Number);
const across = [5, 4, 3, 2, 1, 0];

let min1 = Math.min(...dice);
let min2 = Infinity;
let min3 = Infinity;

for (let i = 0; i < 6; i++) {
    for (let j = i + 1; j < 6; j++) {
        if (j === across[i]) continue; // 두 면이 맞은 편이라면 스킵
        min2 = Math.min(min2, dice[i] + dice[j]);
    }
}

for (let i = 0; i < 6; i++) {
    for (let j = i + 1; j < 6; j++) {
        if (j === across[i]) continue;
        for (let k = j + 1; k < 6; k++) {
            if (k === across[i] || k === across[j]) continue; // 세 면 중 하나라도 맞은 편이라면 스킵
            min3 = Math.min(min3, dice[i] + dice[j] + dice[k]);
        }
    }
}

let sum1 = ((N - 2) * (N - 1) * 4 + (N - 2) ** 2) * min1;
let sum2 = ((N - 1) * 4 + (N - 2) * 4) * min2;
let sum3 = min3 * 4;

// 주사위가 하나인 케이스 고려
console.log(N === 1 ? dice.reduce((acc, cur) => (acc += cur), 0) - Math.max(...dice) : sum1 + sum2 + sum3);

profile
지속적인 성장

0개의 댓글