Algorithm/백준 문제풀이

[백준] 1722 순열의 순서 - Java

너지살 2023. 12. 8. 20:08

 

 

 

문제 출저

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

 

1722번: 순열의 순서

첫째 줄에 N(1 ≤ N ≤ 20)이 주어진다. 둘째 줄의 첫 번째 수는 소문제 번호이다. 1인 경우 k(1 ≤ k ≤ N!)를 입력받고, 2인 경우 임의의 순열을 나타내는 N개의 수를 입력받는다. N개의 수에는 1부터 N

www.acmicpc.net

 

 

 

문제 풀이

이 문제는 2가지 문제를 풀어야 합니다. 

첫 번째 입력은 n이 주어지고 두 번째 입력의 첫 번째 숫자는 어떤 문제를 풀어야 하는지 타입입니다.

1인 경우, k가 주어지며 k번째 순열을 구해야 합니다.

2인 경우, 순열이 주어지며 이 순열이 몇 번째 순서인지 구해야 합니다. 

문제의 n은 20이므로 일일히 다 순열을 구하면 20! 이 되므로 시간초과가 발생합니다. 

 

간격을 통해서 문제를 풀어야 합니다.

먼저, int[] factorial 을 n-1 크기로 만들어 1!, 2!, 3! 같이 각 수의 팩토리얼을 구합니다. 

문제 유형이 2번인 경우에 n = 4, int[] permutation = {1,3,2,4}가 주어졌을 때 예를 들어보겠습니다. 

 

각 자리의 숫자가 오기 까지 factorial[n-i-1] 반복됩니다. 

이게 무슨 말이냐면 1번째 자리인 1이 오기까진 factorial[4 - 0 - 1] * 0 이 반복된 것 입니다.

2번째 자리인 3이 오기까진 factorial[4 - 1 - 1] * 2이 반복된 것 입니다. 

 

이렇게 각 자리수가 오기까지의 횟수를 구하고 1부터 시작했기에 1을 더하면 문제를 풀 수 있습니다. 

 

1번 문제인 경우, 2번 문제를 반대로 풀면 구할 수 있습니다. 

 

저는 해당 블로그의 글을 참고했습니다.

https://dundung.tistory.com/60

 

백준 1722 순열의 순서 Java

순열의 크기 N이 주어지고 순열이 사전순으로 정렬되어 있을 때 1번과 2번으로 나눈 문제가 주어진다. 1번의 경우에는 순열의 순서 k를 주고 k번째 순열 N개를 출력하면 된다. 2번의 경우에는 순열

dundung.tistory.com

 

 

 

소스 코드

package baekjoon.backjoon12.day0110.day08;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

/*
순열의 순서
https://www.aczmicpc.net/problem/1722

20! 이라 일일이 구하면 시간 초과가 발생한다.

참고 사이트
https://dundung.tistory.com/60
 */
public class B1722 {

  static int n;
  static int type;

  static long[] factorial;
  static boolean[] check;

  public static void main(String[] args) throws IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    n = Integer.parseInt(br.readLine());

    StringTokenizer st = new StringTokenizer(br.readLine());
    type = Integer.parseInt(st.nextToken());

    factorial = new long[n+1];
    makeFactorial(n);
    check = new boolean[n+1];

    if(type == 1) {
      long index = Long.parseLong(st.nextToken());
      one(index);
    }
    else if(type == 2) {
      int[] numbers = new int[n];
      for(int i = 0; i < n; i++) {
        numbers[i] = Integer.parseInt(st.nextToken());
      }
      two(numbers);
    }
  }

  // 몇 번째인지 주어지고 순열이 뭔지 구해야한다.
  public static void one(long index) {

    int[] numbers = new int[n];

    for(int i = 0; i < n; i++) {
      for(int j = 1; j <= n; j++) {

        // 이미 사용되었으면 넘어간다.
        if(check[j]) continue;

        // 이 자리의 숫자가 들어갈 수 있다면 뺀다.
        if(factorial[n-i-1] < index) {
          index -= factorial[n-i-1];
        }
        else {
          numbers[i] = j;
          check[j] = true;
          break;
        }
      }
    }
    for(int i = 0; i < n; i++) {
      System.out.print(numbers[i] + " ");
    }
  }

  // 순열이 주어지고 몇 번째 인지 구해야 함
  public static void two(int[] numbers) {
    long index = 1;
    for(int i = 0; i < n; i++) {
      for(int j = 1; j < numbers[i]; j++) {
        if(check[j] == false) {
          index += factorial[n-i-1];
        }
      }
      check[numbers[i]] = true;
    }
    System.out.println(index);
  }

  public static void makeFactorial(int n) {
    factorial[0] = 1;
    for(int i = 1; i <= n; i++) {
      factorial[i] = factorial[i-1] * i;
    }
  }





}