[백준] 11399번 : ATM - JAVA [자바]
11399번: ATM
첫째 줄에 사람의 수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 각 사람이 돈을 인출하는데 걸리는 시간 Pi가 주어진다. (1 ≤ Pi ≤ 1,000)
www.acmicpc.net
-
문제
이 또한 활동 선택 문제(Activity Selection Problem) 문제로 볼 수 있다. 조금만 이해하면 쉽게 풀 수 있는 문제다.
- 알고리즘 [접근 방법]
앞서 포스팅 했던 문제인 회의실배정과 유사한 문제다. 문제를 잘 이해해야 하는 것이 '종료 시간'이 아니라 '대기 시간의 총합'을 구하는 문제라는 점. 즉, 어떻게 배치하던 총 걸리는 같더라도, 한 사람이 대기하는 시간은 달라질 수 있다.
상식적으로 대기시간을 줄이려면 앞 사람이 일찍 끝내야 그만큼 대기시간을 줄일 수 있다. 그리고 각각의 사람이 돈을 인출할 때에는 다른 사람이 개입할 수 없으므로 그리디 알고리즘의 조건인 독립성과 전체 최적해가 부분 최적해를 만족함 둘 다 만족한다.
즉, 입력받은 각각의 인출하는데 걸리는 시간을 오름차순으로 정렬하여 대기시간을 구해주면 되는 전형적인 활동 선택 문제다. 활동 선택 문제에 대해서는 이전 포스팅에서 자세히 다루고 있으니 한 번 보고 오시는 것을 추천드린다.
[백준] 1931번 : 회의실배정 - JAVA [자바]
www.acmicpc.net/problem/1931 1931번: 회의실배정 (1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다. www.acmicpc.net 문제 그리디 알고리즘의 대표적인 문제 중 하나다. 알고리즘 [접근 방법] 위와 같이 시간..
st-lab.tistory.com
알고리즘 자체는 매우 간단하다. 정렬되어있는 배열과, 이전까지의 대기시간 변수, 각 사람별 대기시간의 총합 변수 이렇게 3개를 이용하면 된다.
즉, 아래와 같이 짤 수 있다.
// arr[] 배열은 정렬되어있다고 가정
int prev = 0; // 이전까지의 대기시간 누적합
int sum = 0; // 사람별 대기시간 총합
for(int i = 0; i < N; i++) {
// 이전까지의 대기시간과 현재 사람이 걸리는 시간을 더해준다.
sum += prev + arr[i];
// 이전까지의 누적합에 현재 걸리는 시간을 더해준다.
prev += arr[i];
}
// sum 출력
i번째 사람이 돈을 인출하는데 걸리는 시간 + 이전까지의 대기시간 총합을 하면 i번째가 대기한 시간이 산출된다. 이 것을 sum변수에 더해주면 모든 사람이 돈을 인출하는데 걸렸던 대기시간의 합이 구해진다.
그리고 이전까지의 대기시간을 표현하기 위해 위 과정이 완료되면 prev 변수에 i번째가 돈을 인출하는데 걸렸던 시간을 더해주면 그 다음사람에 대해서는 이전까지의 대기시간이 된다.
- 3가지 방법을 사용하여 풀이한다.
문제를 보면 알겠지만, Pi의 범위가 1 <= Pi <= 1,000 이기 때문에 배열에 넣고 Arrays.sort()을 이용하는 방법과, 카운팅 정렬(Counting Sort)을 쓰는 방법 둘 다 가능하다.
일단, Scanner 입력 방법에서 두 가지 정렬을 각각 테스트 하고, BufferedReader 입력 방법에서는 카운팅 정렬만 사용하여 보여주고자 한다. 즉, 아래와 같은 순서로 풀이 할 것이다.
1. Scanner + Arrays.sort()
2. Scanner + Counting Sort
3. BufferedReader + Counting Sort
- 풀이
- 방법 1 : [Scanner + Arrays.sort()]
import java.util.Scanner;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int[] arr = new int[N];
for(int i = 0; i < N; i++) {
arr[i] = in.nextInt();
}
// 정렬
Arrays.sort(arr);
int prev = 0; // 이전까지의 대기시간 누적합
int sum = 0; // 사람별 대기시간 총합
for(int i = 0; i < N; i++) {
// 이전까지의 대기시간과 현재 사람이 걸리는 시간을 더해준다.
sum += prev + arr[i];
// 이전까지의 누적합에 현재 걸리는 시간을 더해준다.
prev += arr[i];
}
System.out.println(sum);
}
}
가장 기본적인 방법이라 할 수 있겠다. 알고리즘 자체는 그리 어렵지 않으니 쉽게 이해하실 수 있을 것이다.
- 방법 2 : [Scanner + Counting Sort]
정렬 방법을 Arrays.sort() 대신 Counting Sort을 이용하여 풀이하는 방법이다. 혹시 Counting Sort에 대해 모른다면 아래 글을 읽고 오시는 것을 추천드린다.
카운팅 정렬 (Counting Sort / 계수 정렬) _ Java [자바]
Counting Sort [계수 정렬 / 카운팅 정렬] Counting Sort... 계수 정렬, 카운팅 정렬 등으로 많이 불리지만, 가장 와닿는 단어는 카운팅 정렬이므로 이 포스팅에서는 카운팅 정렬로 통일하여 쓰겠다. 카운�
st-lab.tistory.com
쉽게 생각하면 입력받은 값을 index로 활용하는 것이다. 예로들면 입력이 3이라면 index 3의 값을 1 증가시키는 것이다.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
// 입력의 범위는 1 ~ 1000이므로 1001개의 index를 둔다.
int[] arr = new int[1001];
// Counting Sort
while (N-- > 0) {
arr[in.nextInt()]++;
}
int prev = 0; // 이전까지의 대기시간 누적합
int sum = 0; // 사람별 대기시간 총합
for (int i = 0; i < 1001; i++) {
// 해당 i index가 0이 될 때 까지 반복
while (arr[i]-- > 0) {
// 이전까지의 대기시간과 현재 사람이 걸리는 시간을 더해준다.
sum += (i + prev);
// 이전까지의 누적합에 현재 걸리는 시간을 더해준다.
prev += i;
}
}
System.out.println(sum);
}
}
이렇게 입력범위를 알면 유용하게 쓸 수 있는 것이 Counting Sort이니 알아두고 있으면 좋을 것 같다.
- 방법 3 : [BufferedReader + Counting Sort]
입력 방법을 Scanner 대신 BufferedReader 을 사용하여 풀이하는 방법이다. 방법 2와 크게 다른 건 없다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
// 입력의 범위는 1 ~ 1000이므로 1001개의 index를 둔다.
int[] arr = new int[1001];
// Counting Sort
while (N-- > 0) {
arr[Integer.parseInt(st.nextToken())]++;
}
int prev = 0; // 이전까지의 대기시간 누적합
int sum = 0; // 사람별 대기시간 총합
for (int i = 0; i < 1001; i++) {
// 해당 i index가 0이 될 때 까지 반복
while (arr[i]-- > 0) {
// 이전까지의 대기시간과 현재 사람이 걸리는 시간을 더해준다.
sum += (i + prev);
// 이전까지의 누적합에 현재 걸리는 시간을 더해준다.
prev += i;
}
}
System.out.println(sum);
}
}
- 성능
채점 번호 : 23087902 - 방법 3 : BufferedReader + Counting Sort
채점 번호 : 23087895 - 방법 2 : Scanner + Counting Sort
채점 번호 : 23087889 - 방법 1 : Scanner + Arrays.sort()
입력의 경우는 확실히 Scanner 보다는 BufferedReader 가 빠른 걸 볼 수 있다.
- 정리
워낙 간단한 문제라 대체로 쉽게 풀었을 것이다. 정답 비율이 65%인 것 보면 확실히 쉬운 문제인 것 같다. 만약 입력받는 N이 더욱 큰 수가 들어왔다면 정렬에서 시간차이가 확연하게 발생했을텐데 N도 1 ~ 1000까지인지라 크게 차이는 안나는 것 같다.
만약 어렵거나 이해가 되지 않은 부분이 있다면 언제든 댓글 남겨주시면 최대한 빠르게 답변드리겠다.
'JAVA - 백준 [BAEK JOON] > 그리디 알고리즘' 카테고리의 다른 글
[백준] 13305번 : 주유소 - JAVA [자바] (26) | 2021.01.13 |
---|---|
[백준] 1541번 : 잃어버린 괄호 - JAVA [자바] (11) | 2020.10.08 |
[백준] 1931번 : 회의실배정 - JAVA [자바] (44) | 2020.10.05 |
[백준] 11047번 : 동전 0 - JAVA [자바] (9) | 2020.09.25 |