또 전자레인지야?
문제 설명
잇창명의 집에는 오래된 전자레인지가 있다. 백준 온라인 저지에서 문제를 너무 많이 푼 잇창명은 문득 이런 궁금증이 생기기 시작했다.
버튼을 최소 몇 번 눌러야 조리시간 2분을 맞출 수 있을까?
잇창명의 전자레인지에는 다음과 같이 버튼이 4개 있고, 각 버튼을 누르면 다음과 같이 작동한다. 초기 상태에는 조리시간이 0초이고, 조리 중이 아니며, 조리시작 버튼을 눌러야 조리가 시작된다.
- 10초: 조리시간이 10초 늘어난다.
- 1분: 조리시간이 1분(60초) 늘어난다.
- 10분: 조리시간이 10분(600초) 늘어난다.
- 조리시작
- 조리 중이 아닐 때: 조리가 시작된다. 만약에 조리시간이 0초였다면 30초로 늘어난다.
- 조리 중일 때: 조리시간이 30초 늘어난다.
- 모든 버튼은 조리 중인지의 여부와 무관하게 항상 누를 수 있으며, 별도의 언급이 없을 경우 항상 같은 동작을 한다.
예를 들어 이 전자레인지로 2분을 맞추려면 조리시작 버튼을 4번 누르면 되지만, 최적의 방법은 아니다. 그 대신 1분-1분-조리시작 순서로 버튼을 누르면 버튼을 누른 횟수가 3번이 되어 최적이다. 1분-1분의 경우에는 조리가 되지 않기 때문에 최적이 아니다. 실제로는 조리 중에는 남은 조리시간이 계속 줄어들고 중간에 조리를 취소할 수 있지만, 이 문제에서는 생각하지 않기로 한다.
잇창명은 지난 한 학기 동안 전자레인지를 이용할 때마다 매번 문제로 내고 싶은 마음이 들어서 괴로워하고 있다. 잇창명을 도와주자!
입력
첫 줄에 잇창명이 원하는 조리시간이 M:S 형태로 주어진다(0 ≤ M ≤ 60, 0 ≤ S ≤ 59). M은 분, S는 초이며, 항상 두 자리 숫자로 주어진다.
조리시간은 10초 이상 60분(3600초) 이하이며, 항상 10의 배수이다.
출력
주어진 조리시간을 맞추기 위해 버튼을 눌러야 하는 최소 횟수를 출력한다.
제한사항
- 없음
입출력 예
input | return |
02:00 | 3 |
추가 반례 | |
00:00 | 0 |
00:40 | 2 |
문제 분석
시간이 증가하는 조건은 4가지다.
- 10초 증가
- 60초 증가
- 600초 증가
- 30초 증가 (시작)
30초 증가를 안 했으면 시작버튼이 안 눌렸으니 한 번 더 눌러줘야 한다.
조리 시간이 분 초로 주어지는데 계산을 편하게 할 수 있도록 초로 통일해 준다.
항상 주어진 목표가 0일 때를 까먹지 말자.
손으로 풀기
- 계산하기 쉽게 10초를 기준으로 초를 계산해서 목표 시간을 계산한다.
- BFS 방식을 사용한다.
- 현재 시간에 10초단위로 변경해서 반복해서 추가를 한다.
- 더해진 시간이 목표 시간보다 작거나 같고 사용한 적이 없으면 큐에 넣어준다.
- 넣어줄 때 더하는 초가 3으로 시작버튼인 경우 시작 버튼을 누른 것을 표시해서 넣어준다.
- 목표시간에 도달했을 때, 시작 버튼 유무를 확인해 눌리지 않았으면 횟수를 1 더하고 출력한다.
- 목표시간이 0일 경우 버튼 횟수를 0으로 출력하고 아닐 경우, 횟수를 출력한다.
나의 문제풀이
import java.io.*;
import java.util.*;
public class Main {
static boolean[] visited = new boolean[361];
static int time;
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
// 분과 초를 나눠 각각 10초단위를 기준으로 목표시간을 계산한다.
String[] times = bufferedReader.readLine().split(":");
int minute = Integer.parseInt(times[0]) * 6;
int second = Integer.parseInt(times[1]) / 10;
time = minute + second;
int answer = BFS();
// 시작 시간이 0 초이면 버튼을 누를 필요가 없다.
System.out.println(time == 0 ? 0 : answer);
}
public static int BFS() {
// 10초 단위로 시간을 계산한다.
int[] button = {1, 3, 6, 60};
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{0, 0, 0});
while (!queue.isEmpty()) {
int[] temp = queue.poll();
int currentTime = temp[0];
int currentCnt = temp[1];
int start = temp[2];
// 시작 버튼이 눌렸으면 그대로 출력하고 안 눌렸다면, 눌러야할 시간이 있으면 시작 버튼의 횟수를 추가 한다.
if (time == currentTime) {
if (start == 1) return currentCnt;
return currentCnt + 1;
}
for (int i = 0; i < 4; i++) {
int nextTime = currentTime + button[i];
if (nextTime <= time && !visited[nextTime]) {
visited[nextTime] = true;
if (button[i] == 3) { // 시작 버튼을 누르면 확인
queue.offer(new int[]{nextTime, currentCnt + 1, 1});
} else {
queue.offer(new int[]{nextTime, currentCnt + 1, start});
}
}
}
}
return 0;
}
}
한동안 너무 DFS, BFS방식의 문제만 찾아서 풀다가 다른 방식의 문제를 좀 풀기 위해서 다이나믹 프로그래밍 문제를 찾아 풀었는데, 분석을 하다 보니 웬걸 또 BFS방식으로 풀리는 문제였다. 그래도 기존의 BFS들과는 조금은 다르게 생각해야 하는 부분들이 있어 재미있게 풀었다. 그리고 매번 목표에서 아무것도 진행 안 해도 되는 부분을 고려 안 해 매번 한 번씩 틀리는데 다음부터는 잘 생각해서 처음부터 조건을 잘 잡아줘야겠다.
알고리즘 분류
- 다이나믹 프로그래밍
- 그래프 이론
- 그래프 탐색
- 너비 우선 탐색
- 그리디 알고리즘
문제 출처 - https://www.acmicpc.net/problem/24390
24390번: 또 전자레인지야?
첫 줄에 잇창명이 원하는 조리시간이 M:S 형태로 주어진다(0 ≤ M ≤ 60, 0 ≤ S ≤ 59). M은 분, S는 초이며, 항상 두 자리 숫자로 주어진다. 조리시간은 10초 이상 60분(3600초) 이하이며, 항상 10의 배수
www.acmicpc.net
'생각정리 > 코딩테스트' 카테고리의 다른 글
[JAVA 알고리즘]BAEKJOON 7569번 토마토 (1) | 2024.02.01 |
---|---|
[JAVA 알고리즘]BAEKJOON 2527번 직사각형 (1) | 2024.01.30 |
[JAVA 알고리즘]BAEKJOON 1897번 토달기 (1) | 2024.01.18 |
[JAVA 알고리즘]BAEKJOON 2583번 영역 구하기 (0) | 2024.01.17 |
[JAVA 알고리즘]BAEKJOON 5014번 스타트링크 (0) | 2024.01.16 |