본문 바로가기
PS 문제 풀이/Baekjoon

[BOJ] 백준 1021 회전하는 큐

by 까망 하르방 2021. 2. 24.
반응형

출처: https://www.acmicpc.net/problem/1021

 Input 

10 3

2 9 5

 

 Output 

8

덱 = { 1 2 3 4 5 6 7 8 9 10 }이 주어져있고 『2』, 『9』, 『5』를 뽑고자 합니다.

②번 연산 1번 수행 후 『2』를 뽑습니다.  덱 = { 3 4 5 6 7 8 9 10 1 

③번 연산 3번 수행 후 『9』를 뽑습니다.  덱 = { 10 1 3 4 5 6 7 8 

③번 연산 4번 수행 후 『5』를 뽑습니다.  덱 = { 6 7 8 10 1 3 4 

▶ ②, ③ 연산이 총 8번 수행.

 

[구현]

원형 큐를 이용하거나 덱(Dequeue)을 이용.

→ ①번 연산은 덱에서 front 부분 삭제

→ ②, 번은 한쪽에서 pop()한 다음, 다른 쪽에 push()

→ 덱의 상태에서 ②, ③ 연산 중 어떤 것이 효율적인지 판단한다.

 


import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 1 <= N <= 50
        int N = Integer.parseInt(sc.next());
        // 1 <= M <= N
        int M = Integer.parseInt(sc.next());
        
        Queue<Integer> queue = new LinkedList<>();
        // 뽑고자 하는 위치(번호)를 입력 받는다.
        for(int i=0; i<M; i++) {
            queue.add(Integer.parseInt(sc.next()));
         }
        
        // N만큼 덱 원소를 구성한다.
        DQueue DQ = new DQueue();
        for(int i=1; i<=N; i++) {
            DQ.insertRear(i);
        }
        
        int answer = 0;
        while(!queue.isEmpty()) {
            // 뽑고자 하는 번호
            int target = queue.poll();
            
            // 뽑고자 하는 번호의 위치
            // 덱의 내부는 진행되면서 위치가 바뀌기 때문에 그때그때 찾아가야 한다.
            int targetIdx = DQ.searchTarget(target);
            
            
            // 2번연산(왼쪽으로 이동)과 3번연산(오른쪽으로 이동) 중 어떤 것이 나은지 판단한다.
            int opr_2 = targetIdx - 1; // 3번연산을 하였을 때 횟수
            // 제일 뒤로 이동한 후 1번더 이동하여 제일 앞으로 와야 한다.
            int opr_3 = DQ.size() - targetIdx + 1; // 3번연산을 하였을 때 횟수
            
            // 2번연산을 하는 것이 좀 더 최소로 한다면...
            if(opr_2 < opr_3) {
                for(int i=0; i<opr_2; i++) {
                    int val = DQ.deleteFront();
                    DQ.insertRear(val);
                }
                // for문을 돌면서 일일이 answer++할 필요 없다.
                answer = answer + opr_2;
            }
            else { // 3번 연산을 최소로 한다면..
                for(int i=0; i<opr_3; i++) {
                    int val = DQ.deleteRear();
                    DQ.insertFront(val);
                }
                answer = answer + opr_3;
            }
            // 2번연산을 하였든, 3번 연산을 하였든 마지막에는 1번연산으로 번호를 뽑는다.
            // 그래야 다음 숫자를 뽑을 때 덱에 반영된다!
            DQ.remove();
        }
        
        System.out.println(answer);
        
    }
}

class DQNode{
    
    int data;
    DQNode left;
    DQNode right;
    
    public DQNode(int data) {
        this.data = data;
    }
}
class DQueue{
    DQNode front;
    DQNode rear;
    
    // 앞으로 삽입
    void insertFront(int data) {
        DQNode node = new DQNode(data);
        // 비어있다면(처음 넣는 단계라면)
        if(isEmpty()) {
            front = node;
            rear = node;
            return;
        }
        // 제일 앞에 있던 노드는 뒤로 밀려나야 한다.
        node.right = front;
        front.left = node;
        front = node;
    }
    


    // 뒤로 삽입
    void insertRear(int data) {
        DQNode node = new DQNode(data);
        // 비어있다면(처음 넣는 단계라면)
        if(isEmpty()) {
            front = node;
            rear = node;
            return;
        }
        // 제일 뒤에 있던 노드가 앞으로 밀려난다.
        node.left = rear;
        rear.right = node;
        rear = node;
    }
    
    // 앞의 원소를 반환
    int deleteFront() {
        // 현재(기존) front의 데이터를 반환해야 한다.
        int val = front.data;
        // 만약 마지막 노드였다면 모든 연결을 끊는다.
        if(front.right == null) {
            front = null;
            rear = null;
        }else {
            // 오른쪽에서 왼쪽으로의 연결을 끊는다.
            front.right.left = null;
            // front를 오른쪽(뒤)으로 옮긴다.
            front = front.right;
            // 기존 front에서 왼쪽에서 오른쪽 연결은 존재하지만 활용하지 않으므로 상관없다.
        }
        return val;
    }
    
    // 뒤의 원소를 반환
    int deleteRear() {
        // 현재(기존) rear의 데이터를 반환해야 한다.
        int val = rear.data;
        // 만약 마지막 노드였다면 모든 연결을 끊는다.
        if(rear.left == null) {
            front = null;
            rear = null;
        }else {
            // 왼쪽 노드에서 오쪽으로의 연결을 끊는다.
            rear.left.right = null;
            // rear를 왼쪽(앞)으로 옮긴다.
            rear = rear.left;
            /*
             * 위의 두 줄의 코드는 아래와 같이 표현할 수도 있다.
             * rear = rear.left;
             * rear.right = null;
             * 즉, 이동한 다음 연결을 끊은 것이다.
             */
        }
        return val;
    }
    
    // 앞의 원소를 제거(문제의 1번 연산에 해당)
    public void remove() {
        if(front.right == null) {
            front = null;
            rear = null;
        }else {
            front = front.right;
            front.left = null;
        }
    }
    
    // 비어있는지 확인
    boolean isEmpty() {
        return (front == null || rear == null);
    }
    
    // 진행하는 과정에서 뽑고자 하는 번호의 위치들을 탐색
    public int searchTarget(int target) {
        DQNode temp = front;
        // 배열도 아니므로 인덱스를 '1'번부터 하겠다.
        int idx = 1;
        while(temp != null) {
            if(temp.data == target) {
                break;
            }
            // 뒤쪽으로 이동
            idx++;
            temp = temp.right;
        }
        return idx;
    }
    
    // 현재 덱의 크기(제일 뒤의 인덱스를 구하고자 하는 것이다.)
    public int size() {
        DQNode temp = front;
        // 배열도 아니므로 인덱스를 '1'번부터 하겠다.
        int idx = 0;
        while(temp != null) {
            // 뒤쪽으로 이동
            idx++;
            temp = temp.right;
        }
        return idx;
    }
}

 

반응형

'PS 문제 풀이 > Baekjoon' 카테고리의 다른 글

[BOJ] 백준 9012 괄호  (0) 2021.02.24
[BOJ] 백준 1193 분수찾기  (0) 2021.02.24
[BOJ] 백준 5430 AC  (0) 2021.02.24
[BOJ] 백준 2579 계단 오르기  (0) 2021.02.24
[BOJ] 백준 1463 1로 만들기  (0) 2021.02.23

댓글