CodingTEST
[백준 2251] 물통 (JAVA)
경걍
2023. 8. 25. 01:41
반응형
백준 2251번 문제 - 물통
2251번: 물통
각각 부피가 A, B, C(1≤A, B, C≤200) 리터인 세 개의 물통이 있다. 처음에는 앞의 두 물통은 비어 있고, 세 번째 물통은 가득(C 리터) 차 있다. 이제 어떤 물통에 들어있는 물을 다른 물통으로 쏟아 부
www.acmicpc.net
문제 분석

- 물은 C에만 가득찬 상태로 시작
- 물을 옮길 때 물을 받는 쪽이 가득차거나, 물을 주는 쪽이 비는 경우만 가능
- 이 과정을 실행해서 A가 비어있을 때 C의 담긴 물의 양의 경우의 수를 모두 출력
- ex) 0 | 0 | 10 , 0 | 1 | 9 , 0 | 2 | 8 , 0 | 8 | 2 , 0 | 9 | 1
➡️ C의 경우의 수(오름차순) : 1, 2, 8, 9, 10
- ex) 0 | 0 | 10 , 0 | 1 | 9 , 0 | 2 | 8 , 0 | 8 | 2 , 0 | 9 | 1
해결 키 포인트
- BFS(너비 우선 탐색) 개념 파악
- 문제 이해가 일단 어려웠음 → 그냥 쉽게 A 통은 빌 때 가능한 C의 경우의 수를 다 출력
- 어떻게 이들을 동적으로 실행할지에 대해 문제 해결이 어려웠음 (필자는 결국 스스로 풀기를 포기했다)
나의 경험을 토대로 푸는데 도움이 될만한 힌트는 다음과 같다.
- 힌트 1) 가능한 물 움직임은 총 6가지이다 ( A→B, A→C, B→A, B→C, C→A, C→B)
- 힌트 2) 일부 경우의 수만 확인해서는 답이 없다, 가능한 모든 경우의 수를 확인하는 방법을 택해라
- A, B, C 모두 최대 201까지만 커질 수 있음, 각 수(A,B,C)가 0부터 201까지 모든 경우의 수를 확인 - 힌트 3) 힌트 2에서 201을 A, B, C 다 확인하기보다 덜 복잡하게 A, B만 확인해도 된다
( WHY? A, B로 C를 유추 할 수 있기 때문이다) - 힌트 4) 물을 sender에서 receiver로 따를 때, sender의 물 양이 receiver의 최대 물 양보다 많을 경우는 어떻게 처리할까?
- 일단 sender에 있는 물 한계 생각 안하고 그냥 다 붓기
- 최대 물 양이 넘칠 경우, 넘치는 양만큼 sender로 다시 붓고, receiver는 가질 수 있는 최대 물 양으로 설정
너비 우선 탐색(BFS)
그래프 완전 탐색 기번 중 하나로, 그래프의 시작 노드에서 출발해 시작 노드를 기준으로 가까운 노드를 먼저 방문하면서 탐색하는 알고리즘이다.
너비 우선 탐색의 핵심 이론
- 한번 방문한 노드를 다시 방문하면 안되므로 노드 방문 여부를 체크할 배열이 필요
- 그래프는 인접 리스트로 표현
- DFS의 탐색 방식은 선입 선출 특성을 가짐 ( 큐 사용 )
알고리즘
1. BFS를 시작할 노드를 정한 후 사용할 자료구조 초기화

2. 큐에서 노드를 꺼낸 후 꺼낸 노드의 인접 노드를 다시 큐에 삽입 (이를 모든 정점에 다한다)

만약 큐에 노드가 없으면, 방문하지 않은 정점을 알아내, 큐에 삽입하고 다시 2번 단계를 반복한다.
코드
import java.io.*;
import java.util.*;
public class Main {
// 확인해야할 sender -> receiver 정리
static int[] sender = {0,0,1,1,2,2};
static int[] receiver = {1,2,0,2,0,1};
// a가 비워졌을 때 c의 값일 경우에만 true
static boolean [] results = new boolean[201];
static boolean [][] visited = new boolean[201][201];
static int [] input = new int[3];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// A,B,C 입력
StringTokenizer st = new StringTokenizer(br.readLine());
input[0] = Integer.parseInt(st.nextToken());
input[1] = Integer.parseInt(st.nextToken());
input[2] = Integer.parseInt(st.nextToken());
BFS();
// true인 경우만 i 출력 (그때가 a가 비워졌을 때, c가 가능한 값)
for(int i=0;i<=input[2];i++) {
if (results[i]) System.out.print(i + " ");
}
}
static void BFS() {
Queue<AB> queue = new LinkedList<>();
queue.add(new AB(0, 0));
visited[0][0] = true;
results[input[2]] = true;
while(!queue.isEmpty()) {
AB now = queue.poll();
int A = now.A;
int B = now.B;
int C = input[2] - (now.A + now.B);
// 가능한 6개의 케이스 반복
for(int i=0;i<6;i++) {
int[] next = {A,B,C};
// sender에 있는 물 한계 생각 안하고 그냥 다 붓기
next[receiver[i]] += next[sender[i]];
next[sender[i]] = 0;
// 가능한 양이 넘칠 경우,
// 넘치는 양만큼 sender로 이동시키고
// receiver는 가질 수 있는 최댓값으로
int reciver = receiver[i];
if(next[reciver] > input[reciver]) {
next[sender[i]] = next[reciver] - input[reciver];
next[reciver] = input[reciver];
}
int a = next[0];
int b = next[1];
// 아직 확인하지 않은 경우
if(!visited[a][b]) {
queue.add(new AB(a, b));
visited[a][b] = true;
// a가 0인 경우
if(a == 0)
results[input[2] - (a+b)] = true;
}
}
}
}
static class AB {
int A;
int B;
public AB(int A, int B) {
this.A = A;
this.B = B;
}
}
}
반응형