Vienna

백준1158번) 요세푸스 문제 본문

알고리즘 문제 풀이

백준1158번) 요세푸스 문제

아는개발자 2023. 5. 14. 21:17

문제

요세푸스 문제는 다음과 같다.

1번부터 N번까지 N명의 사람이 원을 이루면서 앉아있고, 양의 정수 K(≤ N)가 주어진다. 이제 순서대로 K번째 사람을 제거한다. 한 사람이 제거되면 남은 사람들로 이루어진 원을 따라 이 과정을 계속해 나간다. 이 과정은 N명의 사람이 모두 제거될 때까지 계속된다. 원에서 사람들이 제거되는 순서를 (N, K)-요세푸스 순열이라고 한다. 예를 들어 (7, 3)-요세푸스 순열은 <3, 6, 2, 7, 5, 1, 4>이다.

N과 K가 주어지면 (N, K)-요세푸스 순열을 구하는 프로그램을 작성하시오.

 입력

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

 출력

예제와 같이 요세푸스 순열을 출력한다.


◆ 풀이

List를 2개 준비한다.

첫 번째 List는 순서대로 1부터 N까지 담겨져 있는 리스트.

다른 하나는 결과를 출력할 List.

 

그리고 while문을 돌면서 두 번째 리스트의 개수가 N이 될 때까지 반복한다.

반복하는 내용은 첫 번째 리스트에서 제거될 대상을 구하고, 그 값을 remove한 뒤 두 번째 리스트에 넣어주는 것이다.

 

그렇다면 제거할 대상의 index는 어떻게 구하면 될까?

일단 나온 출력 예상 값을 확인해보자.

<3, 6, 2, 7, 5, 1, 4>

index는 차례대로 2, 4, 1, 3, 2, 0, 0이다.

index로 보았을 때에는 마지막으로 찾은 index값에 +k -1를 한 뒤, 만약 그 값이 list1의 사이즈를 넘어가면 mod 처리하는 것 같다.

 

이 방식으로 코드를 작성해보았다.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String[] strs = scanner.nextLine().split(" ");
        int n = Integer.parseInt(strs[0]);
        int k = Integer.parseInt(strs[1]);

        List<Integer> list1 = new LinkedList<>();
        for(int i=1, iMax = n+1; i<iMax; i++)
        {
            list1.add(i);
        }

        int index = 0;
        List<Integer> list2 = new LinkedList<>();

        while(list2.size()<n){
            index =(index+k-1) % list1.size();
            int value = list1.get(index);
            list2.add(value);
            list1.remove(index);
        }

        System.out.println(list2);
    }
}

하지만 틀렸다고 나왔다.

 

다른 방법이 있는 걸까? 하지만 고민을 거듭해도 딱히 떠오르는 수가 없었다.

그래서 <프로그래밍 대회로 배우는 알고리즘 문제 해결 전략> 책의 조언을 따르기로 결심하고 검색해서 찾아보았다.

대신 아이디어의 시작점만 얻는 것으로 하기로 했다.

 

그리고 한 풀이를 보니 List를 2개 사용해서 풀이하고 있었다.

그리고 앞에서부터 제거 하는 형태로 풀고 있었다.

 

이것을 보니 밀어내기식 아이디어가 떠올라 곧바로 코드로 작성해보았다.

import java.util.*;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String[] strs = scanner.nextLine().split(" ");
        int n = Integer.parseInt(strs[0]);
        int k = Integer.parseInt(strs[1]);

        LinkedList<Integer> list = new LinkedList<>();
        for(int num : IntStream.range(1, n+1).toArray()){
            list.add(num);
        }

        LinkedList<Integer> result = new LinkedList<>();
        int i=0;
        while(result.size()<n){
            int value = list.removeFirst();

            if(++i%k == 0){
                result.add(value);
            }
            else{
                list.add(value);
            }
        }

        System.out.println(result);
    }
}

여전히 틀렸다고 나왔다.

그래서 다시 문제를 노려보다보니...

다소 허탈해졌다.
두 방식 모두 맞았다.

아래는 내가 생각한 방법으로 풀이한 것이고, 

위는 구글링해서 찾은 힌트로 풀이한 것이다.

테스트 케이스가 기본적으로 많은지 시간이 오래걸리기도 했지만 내가 풀이한 방식이 더 간단한 것 같다.

앞으로는 자신감을 가지고 출력이 잘못된 건 아닌지 다시 살펴봐야겠다.

 

오늘의 교훈: 출력 예제를 잘 보자.

Comments