티스토리 뷰

문제

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

풀이과정

N이 주어지면 k번째 순열을 출력하거나 순열을 입력받아서 몇 번째 순열인지 출력하는 문제다.

처음에 문제를 보고 next_permutation을 쓰면 쉽게 풀릴 줄 알고 썻는데 너무 쉽게 풀려고 했나보다 N의 최대값이 20이기 때문에 브루트 포스로 풀면 시간초과가 나버린다.

아래와 같은 알고리즘을 통해 구현을 해야 시간초과가 안나고 해결할 수 있다.

입력 순열로 5 2 4 7 6 3 1 이 주어지면
1 ? ? ? ? ? ? - 6!개 , 2 ? ? ? ? ? ? 6!개, 3 ? ? ? ? ? ? 6!개, 4 ? ? ? ? ? ? 6!개
5 1 ? ? ? ? ? - 5!개
5 2 1 ? ? ? ? - 4!개, 5 2 3 ? ? ? ? - 4!개
5 2 4 1 ? ? ? - 3!개, 5 2 4 3 ? ? ? - 3!개, 5 2 4 6 ? ? ? - 3!개
5 2 4 7 1 ? ? - 2!개, 5 2 4 7 3 ? ? - 2!개,
5 2 4 7 6 1 ? - 1!개
이 과정을 모두 더하면 몇 번째 순열인지 확인할 수 있다. 이러한 과정을 반대로 진행하면 k번째 순열을 출력할 수도 있다.

소스코드

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

using namespace std;

long long f[21]; // 1!~20! 까지 값을 저장할 변수
bool check[21]; // 1~N까지의 숫자가 순열에 있는지 없는지 확인하는 변수

int main()
{
    int N, cmd;

    cin >> N;

    f[0] = 1;

    // 팩토리얼 초기화
    for (int i = 1; i < 21; i++)
        f[i] = f[i - 1] * i;

    cin >> cmd;

    // 몇 번째 순열인지 출력하기.
    if (cmd == 2)
    {
        vector<int> a(N);

        // 순열 입력
        for (int i = 0; i < N; i++)
            cin >> a[i];

        long long ans = 0;

        for (int i = 0; i < N; i++)
        {
            for (int j = 1; j < a[i]; j++)
            {
                // 1부터 해당하는 원소까지 팩토리얼 값을 늘려가며 더해준다.
                if (check[j] == false)
                    ans += f[N - i - 1];
            }
            // 순열에 존재하는 숫자는 있다고 표시해준다.
            check[a[i]] = true;
        }

        cout << ans + 1 << "\n";
    }
    // k 번째 순열 출력하기
    else
    {
        long long k;

        cin >> k;

        vector<int> a(N);

        // k번째 순열 찾는 과정
        for (int i = 0; i < N; i++)
        {
            for (int j = 1; j <= N; j++)
            {
                // 순열에 이미 존재하는 숫자면 넘어간다
                if (check[j] == true) continue;
                // 팩토리얼 값이 k보다 작으면 k에서 팩토리얼 값을 빼준다
                if (f[N - i - 1] < k)
                {
                    k -= f[N - i - 1];
                }
                // 팩토리얼 값이 k보다 크면 해당하는 원소의 숫자를 찾은 것. 
                // a[i]에 저장하고 순열에 존재하는 숫자를 체크해준다
                else
                {
                    a[i] = j;
                    check[j] = true;
                    break;
                }
            }
        }

        for (int i = 0; i < N; i++) cout << a[i] << " ";
        cout << "\n";
    }

    return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 1107번 리모컨  (1) 2018.03.05
[백준] 1476번 날짜 계산  (0) 2018.03.05
[백준] 10974번 모든 순열  (0) 2018.03.04
[백준] 10972번 다음 순열  (0) 2018.03.04
[백준] 6603번 로또  (0) 2018.03.04
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함