카테고리 없음

[백준] 1158: 요세푸스 문제 (python 파이썬)

코딩마루 2024. 4. 6. 10:09
 

1158번: 요세푸스 문제

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)

www.acmicpc.net

해당 문제는 아래와 같이 단순히 구현만 한다면, 시간 초과가 발생한다.

n, k = map(int, input().split())

count = 1
index = 0
result = list()
check_list = [False for _ in range(n)]

while True:
    if not check_list[index]:
        if count == k:
            result.append(str(index+1))
            check_list[index] = True
            count = 1
        else:
            count += 1
    if index >= n-1:
        if False not in check_list:
            break
        index = 0
    else:
        index += 1

print('<'+', '.join(result)+'>')
  • 해당 코드는 0~n-1번 원소에 False를 넣고, 빼낸 숫자들은 True로 바꿔준 코드다. 즉, 이미 나간 숫자들은 True, 아직 나가지 않은 숫자는 False 값을 가진다.
  • 반복문의 종료 조건은 모든 리스트가 True 값을 가져야 한다는 것이다. 이 부분에서 많은 시간이 소요되어 시간 초과가 발생한다. N이 5,000으로 주어진다면, 5,000번의 탐색 이후(원돌기 이후), 다시 5,000개의 각각의 원소가 True인지 확인해야 한다. 이 경우 한 사이클만 25,000,000의 연산을 수행한다는 말이다.

그래서 생각한 방법이 이전에 풀었던 "1406: 에디터"문제의 풀이법이다. 내가 탐색하고 있는 인덱스 기준으로 왼쪽과 오른쪽을 다른 리스트에 보관하는 방식이다. 이전 문제의 풀이는 다음과 같다.

 

[백준] 1406: 에디터 (python 파이썬)

1406번: 에디터 첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할

ngp9440.tistory.com

이 알고리즘을 이용해 큐를 사용하면 다음과 같이 코드를 작성할 수 있다.

from collections import deque

n, k = map(int, input().split())

left_queue = deque()
right_queue = deque([i+1 for i in range(n)])

count = 1
index = 0
result = list()

while True:
    if right_queue:
        if count == k:
            result.append(str(right_queue.popleft()))
            count = 1
        else:
            left_queue.append(right_queue.popleft())
            count += 1
    elif left_queue:
        right_queue = left_queue.copy()
        left_queue.clear()
    else:
        break

print('<'+', '.join(result)+'>')

하지만 해당 문제는 수학적으로 접근하면 더 쉽고 빠르게 풀 수 있다. 알고리즘은 다음과 같다.

  • 1부터 n까지의 원소를 가진 리스트를 만든다.
  • 탐색은 0번 인덱스에서 시작한다.
  • 다음에 없어질 인덱스는 현재 탐색하고 있던 index에서 k-1을 더해주면 된다. 만약 k-1을 더해서 index가 사람 수를 넘어갔다면, index를 사람 수로 나눠 주고, 해당 결과의 나머지 값이 index가 된다. 예를 들어, 1~7번 사람이 주어졌고, k=3이라고 하자. 그럼 먼저 3번(2번 인덱스) 사람이 지워진다. 그 다음 6번(4번 인덱스, 3번 사람이 지워져 인덱스는 한 칸 당겨진다.)이 지워진다. 그리고  6번 인덱스가 지워져야 하는데, 현재 남은 인원은 5명이다. 따라서 그 2번(1번 인덱스) 사람을 지워야 한다.
  • 위 과정을 반복하면 된다.

위 알고리즘을 바탕으로 코드를 작성하면 다음과 같다.

n, k = map(int, input().split())

index = 0
result = list()
check_list = [i+1 for i in range(n)]

while check_list:
    index += k-1
    num_person = len(check_list)
    if index >= num_person:
        index %= num_person
    result.append(str(check_list.pop(index)))
    
print('<'+', '.join(result)+'>')