본문 바로가기
Study (etc)/Problem Solving

[BOJ / C++] 1010번 : 다리 놓기

by Haren 2023. 7. 17.

문제

재원이는 한 도시의 시장이 되었다. 이 도시에는 도시를 동쪽과 서쪽으로 나누는 큰 일직선 모양의 강이 흐르고 있다. 하지만 재원이는 다리가 없어서 시민들이 강을 건너는데 큰 불편을 겪고 있음을 알고 다리를 짓기로 결심하였다. 강 주변에서 다리를 짓기에 적합한 곳을 사이트라고 한다. 재원이는 강 주변을 면밀히 조사해 본 결과 강의 서쪽에는 N개의 사이트가 있고 동쪽에는 M개의 사이트가 있다는 것을 알았다. (N ≤ M)

재원이는 서쪽의 사이트와 동쪽의 사이트를 다리로 연결하려고 한다. (이때 한 사이트에는 최대 한 개의 다리만 연결될 수 있다.) 재원이는 다리를 최대한 많이 지으려고 하기 때문에 서쪽의 사이트 개수만큼 (N개) 다리를 지으려고 한다. 다리끼리는 서로 겹쳐질 수 없다고 할 때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성하라.

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

출력

각 테스트 케이스에 대해 주어진 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.

Solved.ac 레벨

실버 V

풀이

조합 대신 dp로 풀어보기 위해 혼자서 패턴을 찾아보려 끙끙대다 이 분의 블로그 포스팅을 보고 깨달음을 얻었다.

 

[백준 1010번] 다리 놓기

BOJ 1010번 다리놓기 https://www.acmicpc.net/problem/1010백준 1010번 C++ 소스 DP배열을 0으로 초기화할때 memset(DP, 0, sizeof(DP)); 으로 제출했더니 컴파일 에러가나서int DP[33][33] = { 0 }; 이렇게 초기화 했다.i는 N

srccode2.tistory.com

 

dp 배열은 n * m 크기로 선언하여 사용한다. 

dp[n][m]은 서쪽에 n개의 포인트가 있고 동쪽에 m개의 포인트가 있을 때 놓을 수 있는 다리의 수를 나타낸다.

 

먼저 n == m 일 때를 생각해보면 경우의 수는 한 가지 밖에 없다. 다리는 겹쳐서 놓을 수도 없고, 한 포인트에 두 개 이상의 다리가 연결되어서도 안 된다.


그리고 n = 2, m = 3일 경우의 수를 그림으로 그려보며 파악해보자.

 

 먼저 서쪽의 첫 번째 포인트에서 동쪽의 첫 번째 포인트로 다리를 놓았을 경우 남는 경우의 수는 n = 1, m = 2일 때의 경우의 수이고,

서쪽의 두 번째 첫 번째 포인트에서 동쪽의 두 번째 포인트로 다리를 놓았을 때 남는 경우의 수는 n = 1, m = 1 일 때의 경우의 수이며 이는 위에서도 설명했듯 n = 2, m = 2 일 때도 동일하다.

 

즉 n = 2, m = 3, dp[2][3]은 dp[1][2] + dp[2][2]이다.

 

이를 바탕으로 점화식을 세워보면 아래와 같다.

dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]

점화식이 타당한지를 알아보기 위해 n = 3, m = 4일 경우도 위처럼 그림을 그려보면 알 수 있었다. (여기서는 생략)

 

구현할 때에는 n = 1이고 n <= m 일 때의 경우의 수는 m이라는 것을 가지고 dp[1][i]를 초기화해주고 시작하면 된다.

#include<bits/stdc++.h>

using namespace std;

int t;
int dp[31][31];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> t;

    while(t--) {
        int n, m;
        cin >> n >> m;

        for(int i = 1; i <= m; i++) {
            dp[1][i] = i;
            //서쪽의 포인트가 1개이고 동쪽의 포인트가 i개일 때의 경우의 수는 i
        }

        for(int i = 2; i <= n; i++) {
            for(int j = i; j <= m; j++) {
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1];
            }
        }

        cout << dp[n][m] << "\n";
    }

    return 0;
}

 

dp는 익숙해지지가 않는군...

 

 

1010번: 다리 놓기

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

www.acmicpc.net