본문 바로가기

백준/DP

백준 1754 - 진영 순열

728x90

icpc.me/1754

A[0]이 입력으로 주어진 이유

$A[0] \geq 0$ 인 순열들에 대해 진영 순열로 변환하면 다음과 같은 모양새를 띠게 된다.

 

0 _ _ _ ... / _ 0 _ _ ... / _ _ 0 _ ... / _ _ _ 0 ...

 

그러면 a + b = c라고 할 때 c-진영 순열이 되는 경우의 수는

 

0 왼쪽에 있는 순열이 a - 1 진영 순열인 경우 * 0 오른쪽에 있는 순열이 b-진영 순열인 경우

 

여기서 0 왼쪽에 있는 순열은 0때문에 기존 진영 순열에서 1 더해지므로 a - 1 진영 순열이어야 한다.

 

A[0]인 경우도 따로 처리해야 한다. 이럴 때는 길이 n의 수열이 c-진영 순열인 경우를 구하면 된다.

진영 순열 구하기

기존에 순열에서 확장하는 것을 생각해보자. 다음 대소 관계를 가지고 있는 1-진영 순열을 보자

 

$a < b < c < d > e < f$

 

여기서 $g > a, b, c, d, e, f$ 인 g를 넣어서 순열을 확장한다고 할 때 여러가지를 관찰할 수 있다.

 

1. k-진영 순열의 맨 앞에 g를 넣어 만든 순열은 k + 1-진영 순열이다.

 

2. k-진영 순열의 맨 뒤에 g를 넣어 만든 순열은 k-진영 순열이다.

 

3. k-진영 순열에서 대소 관계가 B[i] < B[i + 1] 인 두 수 사이에 g를 넣어 만든 순열은 k + 1-진영 순열이다.

 

4. k-진영 순열에서 대소 관계가 B[i] > B[i + 1] 인 두 수 사이에 g를 넣어 만든 순열은 k-진영 순열이다.

 

우리는 3가지의 갈래로 진영 순열이 확장하는 것을 토대로 다음 dp 테이블을 구성할 수 있다.

 

dp[i][j] := 1번부터 i번까지 순증가 관계에 있는 숫자들을 나열한 순열이 j-진영 순열인 경우의 수

 

이때 0-진영 순열이 되는 경우의 수는 수들이 순증가로 주어지는 점을 고려하면 무조건 1임을 알 수 있다.

 

4가지 경우, 그리고 base case를 고려하여 동적계획법을 수행하면 dp 테이블을 완성할 수 있다.

답 구하기

첫 문단을 참조하되, 여러개의 수를 뽑는 경우를 고려하여 이항계수를 곱해줘야 한다.

 

예를 들어 예제로 주어진 7 3 2의 경우

 

_ _ 0 _ _ _ _ 여기에서 0 오른쪽에 6개의 숫자 중 4개를 뽑은 숫자 조합 $\binom{6}{4}$, 진영 순열을 만드는 경우의 수, 0 왼쪽에 나머지 2개의 숫자로 진영 순열을 만들어진 경우의 수를 곱해준다.

 

K-진영 순열을 만족하지 않는데도 더해주는 것을 유의하도록 하자. 구현을 보면 0의 인덱스가 0이 아닐때 i + j + 1 = K인 경우만 계산을 하고 있다.

 

전체 코드

더보기
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
int n, k, origin;
int limit_l, limit_r;
 
ll jyp[20][20];
int bino[20][20];
 
int getbino(int n, int r);
 
int main()
{
    cin.tie(0); ios::sync_with_stdio(false);
 
    memset(bino, -1sizeof bino);
    bino[1][0= bino[0][0= 1;
 
    cin >> n >> k >> origin;
    
    limit_l = origin;
    limit_r = n - limit_l - 1;
    
    jyp[0][0= 1;
 
    for (int i = 1; i < 20++i)
    {
        for (int j = 0; j < i; ++j)
        {
            if (!j)
                jyp[i][j] = 1;
            else
                jyp[i][j] = (i - j) * jyp[i - 1][j - 1+ jyp[i - 1][j] * (j + 1);
        }
    }
 
    ll res = 0;
 
    if (!origin)
    {
        res = getbino(n - 1, n - origin - 1* jyp[limit_r][k];
    }
    else
    {
        for (int i = 0; i <= k; ++i)
            for (int j = 0; j <= k; ++j)
                if (i + j == k - 1)
                    res += getbino(n - 1, n - origin - 1* jyp[limit_l][i] * jyp[limit_r][j];
    }
 
    cout << res;
}
 
int getbino(int n, int r)
{
    if (n < 0 || r < 0 || n < r)
        return 0;
    
    int &ret = bino[n][r];
 
    if (ret != -1)
        return ret;
    
    return ret = getbino(n - 1, r) + getbino(n - 1, r - 1);
}
cs

728x90

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

[COCI] 백준 10740 - ACM  (0) 2021.01.13
[ICPC] 백준 2135 - 문자열 압축하기  (0) 2021.01.10
백준 1086 - 박성원  (0) 2020.12.31
백준 1351 - 무한 수열  (0) 2020.12.29
[POI] 백준 2287 - 모노디지털 표현  (0) 2020.12.26