문제 설명
일반적인 프린터는 인쇄 요청이 들어온 순서대로 인쇄합니다. 그렇기 때문에 중요한 문서가 나중에 인쇄될 수 있습니다. 이런 문제를 보완하기 위해 중요도가 높은 문서를 먼저 인쇄하는 프린터를 개발했습니다. 이 새롭게 개발한 프린터는 아래와 같은 방식으로 인쇄 작업을 수행합니다.
1. 인쇄 대기목록의 가장 앞에 있는 문서 J를 대기목록에서 꺼냅니다.
2. 나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기목록의 가장 마지막에 넣습니다.
3. 그렇지 않으면 J를 인쇄합니다.
예를 들어, 4개의 문서(A, B, C, D)가 순서대로 인쇄 대기목록에 있고 중요도가 2 1 3 2 라면 C D A B 순으로 인쇄하게 됩니다.
내가 인쇄를 요청한 문서가 몇 번째로 인쇄되는지 알고 싶습니다. 위의 예에서 C는 1번째로, A는 3번째로 인쇄됩니다.
현재 대기목록에 있는 문서의 중요도가 순서대로 담긴 배열 priorities와 내가 인쇄를 요청한 문서가 현재 대기목록의 어떤 위치에 있는지를 알려주는 location이 매개변수로 주어질 때, 내가 인쇄를 요청한 문서가 몇 번째로 인쇄되는지 return 하도록 solution 함수를 작성해주세요.
제한 사항
- 현재 대기목록에는 1개 이상 100개 이하의 문서가 있습니다.
- 인쇄 작업의 중요도는 1~9로 표현하며 숫자가 클수록 중요하다는 뜻입니다.
- location은 0 이상 (현재 대기목록에 있는 작업 수 - 1) 이하의 값을 가지며 대기목록의 가장 앞에 있으면 0, 두 번째에 있으면 1로 표현합니다.
입출력 예
priorities | location | return |
[2, 1, 3, 2] | 2 | 1 |
[1, 1, 9, 1, 1, 1] | 0 | 5 |
입출력 예 설명
예제 #1
문제에 나온 예와 같습니다.
예제 #2
6개의 문서(A, B, C, D, E, F)가 인쇄 대기목록에 있고 중요도가 1 1 9 1 1 1 이므로 C D E F A B 순으로 인쇄합니다.
풀이
큐(LinkedList
)에 대기열의 문서를 순서대로 추가한다.
이때 요청한 문서의 대기목록에서의 위치를 기억해야 하므로 중요도와 인덱스를 Pair
객체로 짝지어 추가한다.
그리고 while
문을 사용하여 위에서 설명한 인쇄 작업을 진행한다.
- 큐에서 맨 앞 문서
front
를 꺼내 놓는다.
중요도가 더 높은 문서를 찾는다면 제거한 뒤 뒤에 다시 추가할 것이고, 그렇지 않으면 그대로 제거할 것이다.
어떤 경우이든 제거해야 하므로peek
메서드가 아니라remove
메서드를 사용한다. for
문으로 큐를 순회하면서front
보다 중요도가 높은 요소가 있는지 확인한다.
찾는다면front
를 큐에 추가하고 1부터 다시 진행한다.- 찾지 못한다면
front
를 인쇄하면 되는 것이므로 인쇄 카운트를 1 증가시킨다.
대기목록에서front
의 위치가location
과 같다면front
가 요청했던 문서라는 뜻이므로 카운트를 리턴한다.
그렇지 않으면 1부터 다시 진행한다.
2에서 1로 돌아가는 방법을 떠올리는 것이 어려웠는데, 처음에는 큐에 변화가 있었음을 나타내기 위해 불리언 변수를 사용했다.
import java.util.LinkedList
fun solution(priorities: IntArray, location: Int): Int {
var answer = 0
val queue = LinkedList<Pair<Int, Int>>()
// (중요도, 대기목록 위치)를 큐에 추가
for (i in priorities.indices) queue.add(priorities[i] to i)
while (queue.isNotEmpty()) {
val n = queue.size
// 중요도에 따라 큐를 조정
for (i in 0..n - 1) {
val frontDocument = queue[0]
var isQueueChanged = false
for (index in 1..n - 1) {
// 맨 앞 문서보다 중요도가 높은 문서를 만나면 제거하고 뒤에 다시 추가
if (queue[index].first > frontDocument.first) {
queue.remove()
queue.add(frontDocument)
isQueueChanged = true
break
}
}
// 큐에 변화가 없으면 조정을 중단
if (!isQueueChanged) break
}
answer++
// 원하는 문서의 위치를 알았다면 중단
if (queue.peek().second == location) break
queue.remove()
}
return answer
}
테스트는 통과했지만 단계마다 불리언 변수를 새로 선언하는 것이 마음에 들지 않았다.
다른 방법이 없는지 알아본 결과, 반복문에 레이블을 붙여 흐름을 제어할 수 있다는 것을 알게 되었다.
효율은 거의 차이가 없지만 코드가 훨씬 간결해지고 알아보기 쉬워졌다.
코드
import java.util.LinkedList
fun solution(priorities: IntArray, location: Int): Int {
var answer = 0
val queue = LinkedList(priorities.mapIndexed { index, priority -> priority to index })
outerLoop@ while (queue.isNotEmpty()) {
val front = queue.remove()
for (doc in queue) {
if (doc.first > front.first) {
queue.add(front)
continue@outerLoop
}
}
answer++
if (front.second == location) break
}
return answer
}
'Problem Solving > Programmers' 카테고리의 다른 글
【프로그래머스】 힙 - 디스크 컨트롤러 (Kotlin) (0) | 2022.07.15 |
---|---|
【프로그래머스】 더 맵게 (Java) (0) | 2022.07.05 |
【프로그래머스】 스택/큐 - 다리를 지나는 트럭 (Kotlin) (0) | 2022.07.05 |
【프로그래머스】 스택/큐 - 기능개발 (Kotlin) (0) | 2022.07.01 |
【프로그래머스】 스택/큐 - 주식가격 (Java) (0) | 2022.07.01 |