[백준] 1966번 : 프린터 큐 - JAVA [자바]
-
문제
- 알고리즘 [접근 방법]
이 번 문제는 위에서 말한 내용을 그대로 큐로 구현만해주면 되는 문제라 그리 어렵지 않게 풀 수 있을 것이다.
문제를 이해해보자면 이렇다.
큐에 프린트 할 문서들이 배치되어있을 때, '중요도'가 높은 것 부터 뽑아야한다는 것이다.
즉, 맨 앞의 문서보다 중요도가 높은 문서가 있을 경우 맨 앞의 문서를 맨 뒤로 보내고, 이를 반복하면서 가장 중요도가 높은 문서가 맨 앞에 배치되도록 해야한다는 것.
이러한 원리로 문서를 중요도가 높은 것 부터 프린트할 때 맨 처음에 찾고자 하던 M번째 문서는 몇 번째로 뽑히는지를 찾아내야하는 것이다.
예로들어 0번째부터 4개의 문서의 중요도가 순서대로 {4, 2, 3, 5}로 주어졌다고 가정하고 두 번째 문서(중요도 : 3)가 몇 번째로 출력되는지를 알아본다고 가정해보자.
위와 같이 알고리즘을 구성하면 된다.
다만, 큐에서 탐색할 때 처음 poll() 된 원소가 가장 큰 경우와, 아닌 경우의 루트가 다르다.
만약 처음 poll() 된 원소가 가장 큰 경우는 해당 원소의 첫 위치가 M이랑 같은지를 비교해야하고,
만약 큐에 poll() 된 원소보다 중요도가 큰 원소가 있는 경우에는 그 원소 앞에 있던 원소들을 뒤로 보낸 뒤, 다시 첫 원소를 poll()하여 비교를 큐 안에 poll() 된 원소보다 중요도가 큰 원소가 있는지를 검사해야 한다.
그렇기 때문에 두 번째 경우에는 해당 원소를 M이랑 같은지 검사할 필요가 없기 때문에 이 분기를 위해서 플래그(Flag) 변수를 하나 두는 것이 좋다.
글로만 설명하면 무슨 말인지 모를 수도 있으니 밑에 코드를 보면서 이해해보도록 해보자.
- 2가지 방법을 사용하여 풀이한다.
이 번에는 Scanner와 BufferedReader 입력만 달리하여 풀이 할 것이다.
출력은 모두 StringBuilder을 사용한다.
1. Scanner
2. BufferedReader
- 풀이
- 방법 1 : [Scanner]
import java.util.Scanner;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
StringBuilder sb = new StringBuilder();
int T = in.nextInt(); // 테스트 케이스
while (T-- > 0) {
int N = in.nextInt();
int M = in.nextInt();
LinkedList<int[]> q = new LinkedList<>(); // Queue로 활용 할 연결리스트
for (int i = 0; i < N; i++) {
// {초기 위치, 중요도}
q.offer(new int[] { i, in.nextInt() });
}
int count = 0;
while (!q.isEmpty()) { // 한 케이스에 대한 반복문
int[] front = q.poll(); // 가장 첫 원소
boolean isMax = true; // front 원소가 가장 큰 원소인지를 판단하는 변수
// 큐에 남아있는 원소들과 중요도를 비교
for(int i = 0; i < q.size(); i++) {
// 처음 뽑은 원소보다 큐에 있는 i번째 원소가 중요도가 클 경우
if(front[1] < q.get(i)[1]) {
// 뽑은 원소 및 i 이전의 원소들을 뒤로 보낸다.
q.offer(front);
for(int j = 0; j < i; j++) {
q.offer(q.poll());
}
// front원소가 가장 큰 원소가 아니였으므로 false를 하고 탐색을 마침
isMax = false;
break;
}
}
// front 원소가 가장 큰 원소가 아니였으므로 다음 반복문으로 넘어감
if(isMax == false) {
continue;
}
// front 원소가 가장 큰 원소였으므로 해당 원소는 출력해야하는 문서다.
count++;
if(front[0] == M) { // 찾고자 하는 문서라면 해당 테스트케이스 종료
break;
}
}
sb.append(count).append('\n');
}
System.out.println(sb);
}
}
가장 기본적인 방법이라 할 수 있겠다.
앞서 말했듯이 front 값이 가장 큰 값이냐, 아니냐에 따라 출력해야 하는 원소인지 아닌지가 갈리기 때문에 isMax 변수를 두었다.
아무래도 반복문이 3중이다보니 조금은 헷갈릴 수 있지만 최대한 주석으로 설명을 해놓았으니 조금만 이해보도록 하자. 이해만하면 어렵지 않다.
그리고 큐의 경우는 중간 탐색(get()) 을 사용해야하므로 LinkedList를 사용했다. 물론, LinkedList도 Queue를 구현하기 때문에 offer, poll 같은 메소드도 사용할 수 있다. offer 대신 add, poll 대신 remove를 사용해도 무방하지만, 큐 문제인만큼 일관성있게 offer와 poll을 사용했다.
- 방법 2 : [BufferedReader]
입력 방법을 Scanner 대신 BufferedReader 을 사용하여 풀이하는 방법이다. 단, BufferedReader 는 문자열을 한 줄로 읽기 때문에 N과 M을 구분하기 위해 공백을 기준으로 문자열을 분리해주어야하니 StringTokenizer 을 사용하여 풀도록 하겠다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int T = Integer.parseInt(br.readLine()); // 테스트 케이스
while (T-- > 0) {
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());
LinkedList<int[]> q = new LinkedList<>(); // Queue로 활용 할 연결리스트
st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {
// {초기 위치, 중요도}
q.offer(new int[] { i, Integer.parseInt(st.nextToken()) });
}
int count = 0; // 출력 횟수
while (!q.isEmpty()) { // 한 케이스에 대한 반복문
int[] front = q.poll(); // 가장 첫 원소
boolean isMax = true; // front 원소가 가장 큰 원소인지를 판단하는 변수
// 큐에 남아있는 원소들과 중요도를 비교
for(int i = 0; i < q.size(); i++) {
// 처음 뽑은 원소보다 큐에 있는 i번째 원소가 중요도가 클 경우
if(front[1] < q.get(i)[1]) {
// 뽑은 원소 및 i 이전의 원소들을 뒤로 보낸다.
q.offer(front);
for(int j = 0; j < i; j++) {
q.offer(q.poll());
}
// front원소가 가장 큰 원소가 아니였으므로 false를 하고 탐색을 마침
isMax = false;
break;
}
}
// front 원소가 가장 큰 원소가 아니였으므로 다음 반복문으로 넘어감
if(isMax == false) {
continue;
}
// front 원소가 가장 큰 원소였으므로 해당 원소는 출력해야하는 문서다.
count++;
if(front[0] == M) { // 찾고자 하는 문서라면 해당 테스트케이스 종료
break;
}
}
sb.append(count).append('\n');
}
System.out.println(sb);
}
}
알고리즘은 완전히 같이 때문에 별달리 설명할 것은 없다.
- 성능
채점 번호 : 26003402 - 방법 2 : BufferedReader
채점 번호 : 26003393 - 방법 1 : Scanner
입력의 경우는 확실히 Scanner 보다는 BufferedReader 가 빠른 걸 볼 수 있다.
- 정리
이 번 문제는 크게 어려운 점이 없었을 것이다. 주어진 문제를 그대로 옮기면 되기 때문에 분기문만 조심해서 쓸 거 외에는 별달리 설명할 것도 없었다.
풀이 방법이야 필자가 말 한 것이 정답은 아니니 다양하게 여러분만의 것으로 만들어 풀어보셔도 좋을 것 같다.
'JAVA - 백준 [BAEK JOON] > 큐, 덱' 카테고리의 다른 글
[백준] 1021번 : 회전하는 큐 - JAVA [자바] (0) | 2021.02.27 |
---|---|
[백준] 10866번 : 덱 - JAVA [자바] (8) | 2021.02.19 |
[백준] 11866번 : 요세푸스 문제 0 - JAVA [자바] (10) | 2021.01.23 |
[백준] 2164번 : 카드2 - JAVA [자바] (4) | 2020.12.19 |
[백준] 18258번 : 큐 2 - JAVA [자바] (5) | 2020.12.13 |