백준의 다리놓기(1010) 문제이다.
[ 문제 바로가기 ]
[ 문제풀이 ]
먼저 간단한 상황들을 통해서 문제 해결방법에 접근해보자.
지금부터는 간략한 표현을 위해서 F[A][B] = C 라는 수식을 이용해서 표현해보자.
F[A][B] = C 의 의미는 "강 왼쪽에 A개의 사이트가 있고 , 강 오른쪽에 B개의 사이트가 있을 때, 다리를 지을 수 있는 경우의 수는 총 C개입니다." 를 의미한다.
또한, 강의 왼쪽에도 , 오른쪽에도 사이트들이 존재하는데 편하게 표시하기 위해서 위에서 부터 1번 ~ N번, 1번 ~ M번 사이트로 표현하겠다.
# Case1 : 왼쪽에 1개의 사이트가 존재하는 Case.
오른쪽에 사이트의 갯수가 M개라고 가정하면, 이 경우 발생 가능한 경우의 수는 M개이다.
왜냐하면 왼쪽에는 1개의 사이트만 존재하고 , 이 1개의 사이트와 연결 가능한 사이트가 M개 이기 때문에 최종적으로 발생 가능한 경우의 수는 M개이다.
F[1][M] = M.
# Case2 : 왼쪽에 2개의 사이트가 있고 오른쪽에 2개의 사이트가 존재하는 Case.
.
경우의 수가 몇가지가 있을까 ?? 딱 봐도 1가지 이다. 서로 마주보고 있는 사이트들끼리 연결하는 방법 1가지 밖에 존재하지 않는다. 즉 ! 왼쪽에 존재하는 사이트의 수와 오른쪽에 존재하는 사이트의 수가 같다면, 다리를 놓을 수 있는 경우의 수는 1가지 이다.
F[A][A] = 1
# Case3 : 왼쪽에 2개의 사이트가 있고 오른쪽에 3개의 사이트가 존재하는 Case.
.
발생 가능한 경우를 모두 만들어 보면 다음과 같은 경우들이 있다.
.
위의 그림과 같이 3가지 경우가 발생한다. 그럼 지금 이야기 한 Case3의 경우의 수를 앞써 이야기한 Case1과 Case2를 통해서 한번 계산을 해보자.
먼저 발생할 수 있는 3가지 경우 중, 다음 2가지 경우를 주목해보자.
.
이렇게 2가지 경우는 한 가지 공통점이 있다. 바로 왼쪽에 있는 1번 사이트를 오른쪽에 있는 1번 사이트로 연결 시켰다는 것이다.
.
위의 그림에서 표시된 파랑색 다리를 공통적으로 갖는다는 것이다.
그럼 ! 저 파랑선분을 하나의 '고정된 다리' 라고 생각해보자.
그럼 왼쪽에 있는 2번 사이트에서는 선택권이 생기게 된다.
.
위의 그림과 같이 2번 사이트에서는 2개의 사이트 중에서 하나를 고를 수 있는 선택권이 생기게 되는 것이다.
파랑색 다리는 고정된 다리니까, 나머지 사이트들만 본다면, 위의 상황은 "왼쪽에 1개의 사이트가 존재하고 , 오른쪽에 2개의 사이트가 존재할 때, 다리를 놓을 수 있는 경우의 수" 를 구하는 상황과 같아진다.
우리는 이 부분을 이미 구해놨다. 바로 Case1 에서 !
왼쪽에 1개의 사이트가 있고, 오른쪽에 M개의 사이트가 있다면 이 때 다리를 놓을 수 있는 경우의 수는 M 가지라고 했다.
Case1에 의하면 위의 상황에서는 오른쪽에 2개의 사이트가 존재하므로 발생 가능한 다리를 놓는 경우의 수는 2가지가 생기게 되는 것이다.
"F[2][3]의 값을 구하기 위해서 F[1][2] 를 사용 !"
그럼 이 경우를 한번 보자.
.
이 경우에서는 왼쪽 1번 사이트가 오른쪽 1번 사이트가 아닌 2번 사이트와 연결되어 있다. 마찬가지로 이 다리를 고정된 다리라고 가정해보자.
.
이렇게 보면 왼쪽 2번 사이트 입장에서는 선택권이 없다. 왜냐하면 연결할 수 있는 사이트가 1개 뿐이기 때문이다.
우리는 이 부분도 이미 계산을 해놨다. Case2 에서 ! 바로 "왼쪽에 존재하는 사이트의 수 = 오른쪽의 존재하는 사이트의 수 일 때, 발생 가능한 경우의 수는 1개이다." 라고 이미 계산을 해놨다.
F[2][3]을 구하기 위해서 F[1][1]을 사용 !
그럼 위의 내용을 수식으로 표현해보면 F[2][3] = F[1][2] + F[1][1] 이 된다. 이 부분 만으로는 일반화된 식을 찾기 힘드니까 한 가지 예시만 더 들어보자.
.
위의 상황을 계산해보자. F[2][3]을 구할 때 처럼, 왼쪽 1번 사이트와 연결된 어떤 사이트의 다리를 고정된 다리라고 가정해보면서 해결해보자.
.
가장 첫 번째로 위와 같이 고정된 다리를 설치했다고 가정해보자.
그럼 발생 가능한 경우의 수는 ??
왼쪽에 2개의 사이트가 있고 오른쪽에 3개의 사이트가 남아있는 형태이기 때문에 F[2][3] 이 된다. 이 또한 위에서 구해놓은 값이다.
.
두 번째로 위와 같이 다리를 고정시켜 놨다고 가정해보자.
그럼 발생 가능한 경우의 수는 왼쪽에 2개의 사이트, 오른쪽에 2개의 사이트가 남아있는 형태이기 때문에 F[2][2]가 되고, 왼쪽 사이트의 수와 오른쪽 사이트의 수가 같기 때문에 이 때는 1가지 이다.
세 번째 상황부터는 다리를 연결할 수 없게 된다. 왜냐하면 왼쪽 1번 사이트와 오른쪽 3~4번 사이트 중 하나가 연결되는 꼴인데, 그렇게 되면 문제에서 제시한 조건대로 다리를 놓을 수가 없게 된다.
정리해보면 F[3][4] = F[2][3] + F[2][2] 로 계산을 하였다.
그럼 위에서 계산한 F[2][3]과 F[3][4]를 통해서 일반화된 식을 한번 찾아보자.
F[2][3] = F[1][2] + F[1][1]
F[3][4] = F[2][3] + F[2][2]
왼쪽에 N개의 사이트가 있다면, F[N][?] = F[N - 1][?] + F[N - 1][?] 의 형태로 나올 것이다.
왜냐하면 ! 위에서 풀었듯이, 우리는 "왼쪽에 있는 1번 사이트를 무조건 고정된 다리로 설치해놓고, 나머지 N - 1개의 사이트들을 연결하는 경우의 수를 구했기 때문" 이다. 그러므로 F[N - 1][?] + F[N - 1][?] 의 형태가 된다.
그럼 뒤에 ? 에는 무슨 값들이 들어갈까 ?? 물음표에는 "오른쪽에 남아있는 사이트의 갯수" 가 된다.
오른쪽에 사이트의 갯수가 M개 였다면 , 왼쪽 1번 사이트와 고정된 다리로 연결된 사이트 1개를 제외하게 되면 결국 M - 1개를 연결하는 형태가 될 것이고, 그 고정된 다리가 오른쪽 사이트 1번에서 2번, 3번, 4번... 순으로 점차 증가할 때 마다, 남은 N - 1개의 사이트에서 선택할 수 있는 다리의 갯수는 점차 감소하게 된다.
따라서 하나의 일반화된 식을 나타내면
F[N][M] = F[N - 1][M - 1] + F[N - 1][M - 2] + ...+ F[N - 1][N - 1] 이 된다.
이를 점화식으로 코드를 구현해 주었다.
3중 for문으로 이를 표현해 주었고, 3개의 포문은 각각 왼쪽 사이트 1 ~ N , 오른쪽 사이트 1 ~ M , 고정된 다리를 제외한 남은 오른쪽 사이트의 갯수로 표현해 주었다.
[ 소스코드 ]
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 | #include <iostream> #include <cstring> #define endl "\n" #define MAX 40 using namespace std; int N, M; int DP[MAX][MAX]; void Initialize() { memset(DP, 0, sizeof(DP)); } void Input() { cin >> N >> M; } void Solution() { for (int i = 1; i <= M; i++) DP[1][i] = i; for (int i = 2; i <= N; i++) { for (int j = i; j <= M; j++) { for (int k = j; k >= i; k--) { DP[i][j] = DP[i][j] + DP[i - 1][k - 1]; } } } cout << DP[N][M] << endl; } void Solve() { int Tc; cin >> Tc; for (int T = 1; T <= Tc; T++) { Initialize(); Input(); Solution(); } } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ BOJ Code ] > # BOJ -' 카테고리의 다른 글
[ 백준 1699 ] 제곱수의 합 (C++) (0) | 2020.08.09 |
---|---|
[ 백준 11057 ] 오르막수 (C++) (0) | 2020.08.09 |
[ 백준 9465 ] 스티커 (C++) (2) | 2020.08.07 |
[ 백준 11052 ] 카드 구매하기 (C++) (10) | 2020.08.06 |
[ 백준 14501 ] 퇴사(Ver2) (C++) (0) | 2020.08.05 |