[백준] 2164번 : 카드2

Darak·2022년 1월 25일
0

https://www.acmicpc.net/problem/2164

문제

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다.

이제 다음과 같은 동작을 카드가 한 장 남을 때까지 반복하게 된다. 우선, 제일 위에 있는 카드를 바닥에 버린다. 그 다음, 제일 위에 있는 카드를 제일 아래에 있는 카드 밑으로 옮긴다.

예를 들어 N=4인 경우를 생각해 보자. 카드는 제일 위에서부터 1234 의 순서로 놓여있다. 1을 버리면 234가 남는다. 여기서 2를 제일 아래로 옮기면 342가 된다. 3을 버리면 42가 되고, 4를 밑으로 옮기면 24가 된다. 마지막으로 2를 버리고 나면, 남는 카드는 4가 된다.

N이 주어졌을 때, 제일 마지막에 남게 되는 카드를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 정수 N(1 ≤ N ≤ 500,000)이 주어진다.

6

출력

첫째 줄에 남게 되는 카드의 번호를 출력한다.

4

풀이

먼저 1부터 n까지를 큐에 넣는다.

for (int i = 0; i < n; i++)
	queue.add(i + 1);

큐에 넣는 이유는 큐가 FIFO(First In First Out)구조이므로 문제에서 주어진 연산 구현이 편리하기 때문이다.
'맨 위의 카드를 버리는 것'은 큐의 dequeue 연산을(Java에서는 remove()/poll()), 맨 위의 카드를 '다시 아래로 집어넣는 것'은 큐의 enqueue 연산(Java에서는 add()/offer())을 이용하면 쉽게 구현할 수 있다.

연산의 차이

add() / offer()

  • add(): 용량 부족 시 예외 발생
  • offer(): 용량 부족 시 return false, 예외 발생 x

remove() / poll()

  • remove(): 큐가 비었으면 예외 발생
  • poll(): 큐가 비었으면 return null, 예외 발생 x

Ver. 1

간단하게 생각하면 '마지막 하나가 남을 때까지' 문제에 주어진 그대로를 반복하면 되므로 이렇게 구현할 수 있다.

while (true) {
	lastCard = queue.remove();
	if(queue.isEmpty())
		break;
	queue.add(queue.remove());
}

맨 위의 카드를 버리는 것을 remove()로 구현하고, 그 다음 맨 위의 카드를 맨 아래에 집어넣는 것을 add()로 구현할 수 있다. remove() 이후 큐가 비었다는 것은 그 카드가 마지막 카드였다는 것이므로 루프를 종료한다.

Ver. 2

마지막 하나의 카드가 남기까지 문제에서 주어진 동작을 몇 번 반복해야 하는지 생각해 보면, 카드가 1개이면 0번, 2개이면 1번, 3개이면 2번, 4개이면 3번, ... 을 반복해야 한다. 즉 카드가 n개일 때 주어진 동작을 n-1번 반복해야 함을 알 수 있다.

for (int i = 0; i < n - 1; i++) {
	queue.remove();
	queue.add(queue.remove());
}

이후 큐에 남아있는 마지막 카드를 remove()로 출력하면 된다.

전체 코드

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

public class BJ_2164 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		Queue<Integer> queue = new LinkedList<>();
		int n = sc.nextInt();
		//int lastCard = 0, bottomCard = 0;
		
		for (int i = 0; i < n; i++)
			queue.add(i + 1);
		
 		//Ver. 1
		/*while (true) {
			lastCard = queue.remove();
			if (queue.isEmpty())
				break;
			bottomCard = queue.remove();
			queue.add(bottomCard);
		}

		System.out.println(lastCard);*/
		
		//Ver. 2
		for (int i = 0; i < n - 1; i++) {
			queue.remove();
			queue.add(queue.remove());
		}
		
		System.out.println(queue.remove());
	}

}

+

이번 문제도 여러 가지를 배울 수 있었다. 자바로 큐를 구현하려면 반드시 LinkedList를 이용해야 하는 것과, 자바의 큐에 대한 기본 연산들, 그리고 기본 연산들의 차이에 대해서도 알아볼 수 있는 좋은 기회가 되었다.

그리고 이번에도 다른 사람의 코드가 많은 공부가 되었다. 그냥 대충 짜고 맞았다~ 끝!으로 끝내는 것이 아니라 조금 더 효율적이고 '좋은' 코드에 대해 고민해 보아야 한다는 것을 느낄 수 있었다.

0개의 댓글