[ 백준 ] 1722 / 순열의 순서

金弘均·2021년 9월 15일
0

Baekjoon Online Judge

목록 보기
125/228
post-thumbnail
post-custom-banner

# Appreciation

/*
 * Problem :: 1722 / 순열의 순서
 *
 * Kind :: Math
 *
 * Insight
 * - N=4 일 때, (x는 임의의 숫자)
 *   xxxx 인 경우는 4! = 24 가지
 *   1xxx 인 경우는 3! = 6  가지
 *   12xx 인 경우는 2! = 2  가지
 *   123x 인 경우는 1! = 1  가지
 *   + 1xxx 인 경우 3!
 *     2xxx 인 경우 3!
 *     3xxx 인 경우 3!
 *     4xxx 인 경우 3!
 *     k=19 라면, 첫 번째 숫자는
 *     A = [1, 2, 3, 4] 에서
 *     A[(19-1) / 3!] = 4 임을 알 수 있다
 *     # k -= 3*3!
 *       이제 4xxx 에서 k=1 인 순열을 찾으면 된다
 *       -> 반복
 *
 * - 소문제 번호 1
 *   N=4, k=3 일 때를 생각해보자 (예제 입력 1)
 *   [1, 2, 3, 4] 에서 3 번째 순열을 구해야 한다
 *   + 1234 - 1
 *     1243 - 2
 *     1324 - 3
 *     1342 - 4
 *     1423 - 5
 *     1432 - 6
 *     2134 - 7
 *     2143 - 8
 *     ...
 *     # 1 ~ 6(1*3!)번째 까지가 1로 시작한다
 *       7 ~ 12(2*3!)번째 까지가 2로 시작한다
 *       ...
 *       -> 첫번째 숫자는 1이다
 *          k -= (k-1)/3! * 3!
 *   1 [2, 3, 4] 에서 3 번째 순열을 구해야 한다
 *   + 234 - 1
 *     243 - 2
 *     324 - 3
 *     342 - 4
 *     ...
 *     # 1 ~ 2(1*2!)번째 까지가 2로 시작한다
 *       3 ~ 4(2*2!)번째 까지가 3으로 시작한다
 *       ...
 *       -> 두번째 숫자는 3이다
 *          k -= (k-1)/2! * 2!
 *  1 3 [2, 4] 에서 1 번째 순열을 구해야 한다
 *  + 24 - 1
 *    42 - 2
 *    # 1 ~ 1(1*1!)번째 까지가 2로 시작한다
 *      2 ~ 2(2*1!)번째 까지가 4로 시작한다
 *      -> 세번째 숫자는 2이다
 *         k -= (k-1)/1! * 1!
 *  1 3 2 [4] 에서 1 번째 순열을 구해야 한다
 *  + 4 - 1
 *    # 1 ~ 1(1*0!)번째 까지가 4로 시작한다
 *      -> 네번째 숫자는 4이다
 *         k -= (k-1)/0! * 0!
 *
 * - 소문제 번호 2
 *   N=4, [1, 3, 2, 4] 일 때를 생각해보자 (예제 입력 2)
 *   + (1, 2, 3, 4) 중 1의 인덱스는 0 이다
 *     k += 0 * 3!
 *   + (2, 3, 4) 중 3의 인덱스는 1 이다
 *     k += 1 * 2!
 *   + (2, 4) 중 2의 인덱스는 0 이다
 *     k += 0 * 1!
 *   + (4) 중 4의 인덱스는 0 이다
 *     k += 0 * 0!
 *   + 위처럼 생각하면 [1, 2, 3, 4] 는 k=0 이므로
 *     k++
 *
 * Point
 * - 소문제 번호 1
 *   + N=4 일 때, 1234 를 k=1 이 아닌 k=0 으로 생각하자
 *     이렇게 하면, 모듈러 연산도 편해지고 생각하기 쉬워진다
 *     # 소문제 번호 2 Insight 설명에서
 *       마지막에 k++ 를 더한것과
 *       소문제 번호 1 Insight 설명에서
 *       k 가 1이 남는 것은 같은 이치다
 *       -> 편의성을 위해 [1, 2, 3, 4] 를 k=1 로 볼 것이냐 k=0 으로 볼 것이냐의 차이다
 *
 * -       20! = 2432902008176640000
 *   max(long) = 9223372036854775807
 *   long 을 사용해서 Overflow 를 사전에 방지하자
 */

# Code

//
//  BOJ
//  ver.C++
//
//  Created by GGlifer
//
//  Open Source

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define endl '\n'

// Set up : Global Variables
/* None */

// Set up : Functions Declaration
long f(int n);


int main()
{
    // Set up : I/O
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // Set up : Input
    int N; cin >> N;

    int cmd; cin >> cmd;
    /* 소문제 번호 1 */
    if (cmd == 1) {
        long k; cin >> k;

        // Process
        vector<int> A(N, -1); /* k 번째 순열이 저장될 Vector */
        vector<int> nums; /* 현재 남아있는 사용가능한 숫자들 */
        for (int i=1; i<=N; i++) {
            nums.push_back(i);
        }

        k--; /* [1, 2, ... , N] 을 k=0 으로 간주 */
        for (int i=0; i<N; i++) {
            long q = k / f(N-1-i); /* [q*k/f(N-1-i), (q+1)*k/f(N-1-i)) 번째 까지가 */
            A[i] = nums[q]; /* nums[q] 로 시작함, 따라서 A[i] 에 nums[q] 를 할당함 */
            nums.erase(nums.begin()+q); /* nums[q] 에 해당하는 숫자는 사용되었기에 지워줌 */
            k %= f(N-1-i); /* k 값 갱신 */
        }

        // Control : Output
        for (int n : A) {
            cout << n << ' ';
        } cout << endl;
    }
    /* 소문제 번호 2 */
    else if (cmd == 2) {
        int A[N];
        for (int i=0; i<N; i++) {
            cin >> A[i];
        }

        // Process
        vector<int> nums; /* 현재 남아있는 사용가능한 숫자들 */
        for (int i=1; i<=N; i++) {
            nums.push_back(i);
        }

        long k = 0; /* [1, 2, ... , N] 을 k=0 으로 했을 때 몇 번째 순열인지 저장됨 */
        for (int i=0; i<N; i++) {
            /* 현재 사용가능한 남아있는 수들 중 A[i] 의 위치를 찾음 */
            auto pos = find(nums.begin(), nums.end(), A[i]);
            int idx = pos - nums.begin(); /* 위치(이터레이터)를 인덱스로 변환 */
            k += idx * f(N-1-i); /* k 값 갱신 */
            nums.erase(pos); /* pos 에 있던 숫자는 사용되었으므로 지워줌 */
        } k++; /* [1, 2, ... , N] 을 k=0 으로 간주했으므로 1을 더해줌 */

        // Control : Output
        cout << k << endl;
    }
}

// Helper Functions
long f(int n)
/* (long)n! 을 반환 */
{
    if (n == 0) return 1;
    return (long)n * f(n-1);
}
profile
이런 미친 게임을 봤나! - 옥냥이
post-custom-banner

0개의 댓글