3월 10, 2024

[백준] 1158번 조세퍼스 문제 Queue 사용해서 풀어보기

1. 문제

1) 링크

www.acmicpc.net/problem/1158

2) 문제

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

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

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

3) 입력

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

4) 출력

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

 

더 자세한 문제의 조건을 알아보기 위해서 위의 링크를 클릭해보자


2. 풀이

Queue는 First In, First Out의 구조를 가지고 있는 자료구조이다.

 

위의 조세퍼스 문제는 꼭 Queue로 풀 필요는 없으나 Queue의 성질을 사용하여서 유용하게 풀 수 있는 문제 중 하나이다. 

 

예를 들어서 m이 4였다면,

queue에 3번 pop하고 push를 한 뒤에 마지막에 pop 한 것을 String에 추가해주면 된다. 

즉 m-1번을 pop, push를 한 뒤에 마지막 한번 다시 pop을 해주면 되는 것이다.

 

그리고 이것을 총 n-1번 시행한다면 우리가 원하는 조세퍼스 문제의 답을 구할 수 있는 것이다.


3. 코드 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        Queue<Integer> q=new LinkedList<Integer>();
        for(int i=1; i<=n; i++){
            q.offer(i);
        }
        StringBuilder sb=new StringBuilder();
        sb.append('<');
        for(int i=0; i<n-1; i++){
            for(int j=0; j<m-1; j++){
                q.offer(q.poll());
            }
            sb.append(q.poll()+", ");
        }
        sb.append(q.poll()+">");
        System.out.println(sb);
    }
}

queue에서는 push를 offer() , pop에서 poll() 이라는 메서드를 사용한다.

 

여기서 n번을 총 시행해야 하는데 for문을 n-1 번 사용한 것은 단순히 마지막 부분의 것에는 ">" 를 붙여주어야 하기 때문이다. 

n번을 돌고 마지막에 ">"만 붙여주어도 상관 없다.

 

특히 poll() 같은 경우에는 그래프 BFS를 풀 때도 자주 등장하는 것이기 때문에 꼭 까먹지 말고 기억하자.


2월 28, 2024

[백준] 14226번 이모티콘 문제 BFS의 변형

1. 문제

1) 링크

www.acmicpc.net/problem/14226

2) 문제

영선이는 매우 기쁘기 때문에, 효빈이에게 스마일 이모티콘을 S개 보내려고 한다.

영선이는 이미 화면에 이모티콘 1개를 입력했다. 이제, 다음과 같은 3가지 연산만 사용해서 이모티콘을 S개 만들어 보려고 한다.

  1. 화면에 있는 이모티콘을 모두 복사해서 클립보드에 저장한다.
  2. 클립보드에 있는 모든 이모티콘을 화면에 붙여넣기 한다.
  3. 화면에 있는 이모티콘 중 하나를 삭제한다.

모든 연산은 1초가 걸린다. 또, 클립보드에 이모티콘을 복사하면 이전에 클립보드에 있던 내용은 덮어쓰기가 된다. 클립보드가 비어있는 상태에는 붙여넣기를 할 수 없으며, 일부만 클립보드에 복사할 수는 없다. 또한, 클립보드에 있는 이모티콘 중 일부를 삭제할 수 없다. 화면에 이모티콘을 붙여넣기 하면, 클립보드에 있는 이모티콘의 개수가 화면에 추가된다.

영선이가 S개의 이모티콘을 화면에 만드는데 걸리는 시간의 최솟값을 구하는 프로그램을 작성하시오.

3) 입력

첫째 줄에 S (2 ≤ S ≤ 1000) 가 주어진다.

4) 출력

첫째 줄에 이모티콘을 S개 만들기 위해 필요한 시간의 최솟값을 출력한다.

 

문제의 세부조건을 더 알아보기 위해서는 위의 링크에 들어가서 확인해보자.


 2. 풀이

이 문제는 중요한 변수가 크게 두 가지가 있다. 첫번째는 화면에 있는 이모티콘의 개수, 두번째는 클립보드에 있는 이모티콘의 개수이다. 할 수 있는 연산 또한 클립보드의 개수에 따라서 결과가 달라지기 때문에 이 문제는 화면이모티콘의 개수와 클립보드에 있는 이모티콘의 개수를 함께 queue에 넣어주어야 한다. while문을 돌때도 두 개를 함께 pop 해주어야 한다는 것을 알 수 있다. 

 

그렇다면, 할 수 있는 시행을 하나씩 보면서 화면이모티콘의 개수와 클립보드 이모티콘의 개수가 어떻게 변화하는지 알아보자

  1. 화면에 있는 이모티콘을 모두 복사해서 클립보드에 저장한다. : (화면, 클립) --> (화면, 화면)
  2. 클립보드에 있는 모든 이모티콘을 화면에 붙여넣기 한다.: (화면, 클립)--> (화면+클립, 클립)
  3. 화면에 있는 이모티콘 중 하나를 삭제한다. : (화면, 클립)--> (화면-1, 클립)

위와 같이 변한다는 것을 알 수 있다. 

시작은 화면이모티콘 1개와 클립보드 이모티콘 0개로 시작한다. 

따라서 queue 에 1과 0을 각각 push해준 채로 시작하면 된다.

 

그리고 가능한 3가지의 경우를 모두 다 queue로 처리해준다.

 while(!q.isEmpty()){
  int s=q.remove();
    int c=q.remove();
    if (time[s][s]==-1){ //첫번째 복사
   time[s][s]=time[s][c]+1;
  q.add(s);q.add(s);
            }
   if (s+c<=goal&&time[s+c][c]==-1){ //두번째 붙여넣기
     time[s+c][c]=time[s][c]+1;
     q.add(s+c); q.add(c);
            }
    if (s>=1 && time[s-1][c]==-1){ //세번째 삭제
  time[s-1][c]=time[s][c]+1;
q.add(s-1); q.add(c);
            }
        }

세가지를 모두 다 처리하면 위와 같다.

대부분의 bfs 문제는 한 가지 변수만 queue로 처리하지만 이 경우는 답이 두 가지 변수에 의해 영향을 받기 때문에 두 가지를 queue에 push, pop해준다는 점이 특이하다.

 

3. 코드 

전체 코드를 살펴보면 아래와 같다. 

import java.util.*;

public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int goal=sc.nextInt();
        int time[][]=new int[goal+1][goal+1]; // 화면이모티콘과 클립보드 이모티콘 개수 저장
        for(int i=0; i<=goal; i++){
            for(int j=0; j<=goal; j++){
                time[i][j]=-1; //처음 시간은 -1로 초기화
            }
        }
        Queue<Integer> q= new LinkedList<>();
        q.add(1);
        q.add(0);
        time[1][0]=0;
        while(!q.isEmpty()){
            int s=q.remove();
            int c=q.remove();
            if (time[s][s]==-1){
                time[s][s]=time[s][c]+1;
                q.add(s);q.add(s);
            }
            if (s+c<=goal&&time[s+c][c]==-1){
                time[s+c][c]=time[s][c]+1;
                q.add(s+c); q.add(c);
            }
            if (s>=1 && time[s-1][c]==-1){
                time[s-1][c]=time[s][c]+1;
                q.add(s-1); q.add(c);
            }
        }
        int ans=-1;
        for(int i=0; i<=goal; i++){
            if (time[goal][i]!=-1){
                if ((ans==-1)|| (ans> time[goal][i])){
                ans=time[goal][i];
            }
            }
            
        }
        System.out.println(ans);
    }
}