본문 바로가기
코딩 테스트/백준

2251 물통

by 위그든씨 2024. 12. 27.

각각 부피가 A, B, C(1≤A, B, C≤200) 리터인 세 개의 물통이 있다. 처음에는 앞의 두 물통은 비어 있고, 세 번째 물통은 가득(C 리터) 차 있다. 이제 어떤 물통에 들어있는 물을 다른 물통으로 쏟아 부을 수 있는데, 이때에는 한 물통이 비거나, 다른 한 물통이 가득 찰 때까지 물을 부을 수 있다. 이 과정에서 손실되는 물은 없다고 가정한다.

이와 같은 과정을 거치다보면 세 번째 물통(용량이 C인)에 담겨있는 물의 양이 변할 수도 있다. 첫 번째 물통(용량이 A인)이 비어 있을 때, 세 번째 물통(용량이 C인)에 담겨있을 수 있는 물의 양을 모두 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 세 정수 A, B, C가 주어진다.

출력

첫째 줄에 공백으로 구분하여 답을 출력한다. 각 용량은 오름차순으로 정렬한다.

====

문제 분석

하노이의 탑이랑 같은 유형의 문제이다. 

어떤 지점에서 다른 지점으로 물을 옮길 수 있냐를 묻는 문제 

각 물통에 담을 수 있는 물의 총량이 8 9 10이면, 처음에는 C에만 담겨 있으므로 [0,0,10] 을큐에 담아준다.

이 다음에 취할 수 있는 것은 c의 물을 a에 담기/b에 담기이다. 

그렇다면 [8,0,2] , [0,9,1]이라는 경우의 수가 나옴 

[8,0,2] 에서는 [0,8,2] , [0,0,10]이 나오는데 0,0,10은 중복 되니 배제 

[0,9,1] 에서는 [8,1,1] , [0,0,10] 이 나오는데 여기서도 0,0,10은 배제 

중복을 체크하는 방법은 3중 배열을 선언하여 체크 => visited[a][b][c] -> 여기서 a,b,c는 담을 수 있는 물의 총량

const LIMIT = input[0].split(' ').map((v) => +v);
const answer = [];
const q = [[0, 0, LIMIT[2]]];
const visited = Array.from({ length: LIMIT[0] + 1 }, () =>
    Array.from({ length: LIMIT[1] + 1 }, () =>
        Array.from({ length: LIMIT[2] + 1 }).fill(false)
    )
);
visited[0][0][LIMIT[2]] = true;
while (q.length) {
    const arr = q.shift();
    if (arr[0] === 0) {
        answer.push(arr[2]); 	// a가 0일때의 c의 물양을 출력해야 하므로 
    }
    for (let alpha = 0; alpha < 3; alpha++) {
        if (arr[alpha] === 0) continue;
        for (let otherAlpha = 0; otherAlpha < 3; otherAlpha++) {
            if (otherAlpha === alpha) continue;
            const diff = Math.min(
                LIMIT[otherAlpha] - arr[otherAlpha],
                arr[alpha]
            );
            const temp = arr.map((v, idx) => {
                if (idx === alpha) return v - diff;
                if (idx === otherAlpha) return v + diff;
                return v;
            });
            const [a, b, c] = temp;
            if (visited[a][b][c]) continue;
            visited[a][b][c] = true;
            q.push([a, b, c]);
        }
    }
}
console.log(answer.sort((a, b) => a - b).join(' '));

위에 선언 된 diff는 다른 위치로 물을 옮길 때 넘치면 안되어서 LIMIT-arr 을 해준 것이고,

이때 옮길 수 있는 물의 양보다 많은 양을 옮길 수는 없으므로 Math.min을 통해 작은 값을 옮겨준다.

정답 코드

const main = (input) => {
    const LIMIT = input[0].split(' ').map((v) => +v);
    const answer = [];
    const q = [[0, 0, LIMIT[2]]];
    const visited = Array.from({ length: LIMIT[0] + 1 }, () =>
        Array.from({ length: LIMIT[1] + 1 }, () =>
            Array.from({ length: LIMIT[2] + 1 }).fill(false)
        )
    );
    visited[0][0][LIMIT[2]] = true;
    while (q.length) {
        const arr = q.shift();
        if (arr[0] === 0) {
            answer.push(arr[2]);
        }
        for (let alpha = 0; alpha < 3; alpha++) {
            if (arr[alpha] === 0) continue;
            for (let otherAlpha = 0; otherAlpha < 3; otherAlpha++) {
                if (otherAlpha === alpha) continue;
                const diff = Math.min(
                    LIMIT[otherAlpha] - arr[otherAlpha],
                    arr[alpha]
                );
                const temp = arr.map((v, idx) => {
                    if (idx === alpha) return v - diff;
                    if (idx === otherAlpha) return v + diff;
                    return v;
                });
                const [a, b, c] = temp;
                if (visited[a][b][c]) continue;
                visited[a][b][c] = true;
                q.push([a, b, c]);
            }
        }
    }
    console.log(answer.sort((a, b) => a - b).join(' '));
};
const input = require('fs')
    .readFileSync(process.platform === 'linux' ? '/dev/stdin' : './input.txt')
    .toString()
    .trim()
    .split('\n');

main(input);

'코딩 테스트 > 백준' 카테고리의 다른 글

1931 회의실 배정  (0) 2024.12.30
8111 0과 1  (0) 2024.12.30
1027 고층 건물  (1) 2024.12.27
11967 불 켜기 (자바스크립트)  (0) 2024.12.26
11559 Puyo Puyo (자바스크립트)  (0) 2024.12.24