View

[백준] 1010 - 다리 놓기

sm_amoled 2021. 12. 25. 13:01
300x250

문제링크

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

문제

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

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

입력

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

출력

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

조건

  • 시간 제한 : 0.5s
  • 메모리 제한 : 128MB

해설

M이 8일 때 N의 값에 따른 다리를 지을 수 있는 경우를 따져보자.

N = 1 : 8
N = 2 : 7 + 6 + 5 + 4 + 3 + 2 + 1
N = 3 : (6 + ... + 1) + (5 + ... + 1) + ... + 1
N = 4 : {(5+...+1)+(4+...+1)+...+1} + {(4+...+1)+...+1}+...+1
...

여기에서 찾을 수 있는 규칙은 N이 증가할수록 N-1에서 구한 값들이 부분적으로 사용된다는 것이다. N=3일 때 첫번째 괄호의 값은 N이 2일 때의 1번째 원소를 제외한 나머지의 합과 같고, 두번째 괄호의 값은 2번째 원소까지 제외한 나머지의 합과 같다. 즉, N[k]의 값은 N-1[k+1 ~ M]의 합과 같다.

풀이

먼저 M 값 크기의 vector를 만들어 1~M의 값을 역순으로 넣어준다.

vector<long> dp(M);    
for(int i = 0; i < M; i++) {
    dp[i] = M-i;
}

반복문을 통해 N[k]의 값은 N-1[k+1 ~ M]임을 구현하여준다. i는 N의 값에 따라 dp의 단계가 올라감을, j는 현재 바꾸고자 하는 dp값을, k는 N-1에 해당하는 값들을 가져오는 역할을 한다. 반복문을 통해 i 단계에서 dp[j] 에는 i-1 단계의 dp[j+1 ~ M] 값의 합으로 초기화된다.

for(int i = 2; i < N; i++) {
    for(int j = 0; j < M; j++) {
        long temp = 0;
        for(int k = j+1; k < M; k++) {
            temp += dp[k];
        }
        dp[j] = temp;
    }   
}
M이 8일 때 dp 벡터에 담겨있는 값
i = 2 : 8 / 7 / 6 / 5 / 4 / 3 / 2 / 1
i = 3 : (6 + ... + 1) / (5 + ... + 1) / ... / 1
i = 4 : {(5+...+1)+(4+...+1)+...+1} / {(4+...+1)+...+1} / ... /1
...

여기에서 만약 구하고자 하는 조건의 N값이 4라면, 계산하여야 하는 값은 {(4+...+1)+...+1} + {(3+2+1)+(2+1)+1} + {(2+1) + 1} + 1 이고, 이 값은 i=4 단계의 첫번째 원소를 제외한 나머지 원소의 합과 같다. 이를 식으로 나타내면 아래와 같이 나타낼 수 있다.

long sum = 0;
for(int i = 1; i < M; i++) {
    sum += dp[i];
}
cout << sum << '\n';

이를 T번 반복하면 된다!


코멘트

이게 DP 푸는 맛인가..?


코드

#include <iostream>
#include <vector>

using namespace std;

int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0); cout.tie(0);
    
    int T;
    cin >> T;
    
    while(T--) {
        int N, M;
        cin >> N >> M;
        
        if(N == 1) {
            cout << M << '\n';
            continue;
        }
        
        vector<long> dp(M);    
        for(int i = 0; i < M; i++) {
            dp[i] = M-i;
        }
        
        
        for(int i = 2; i < N; i++) {
            for(int j = 0; j < M; j++) {
                long temp = 0;
                for(int k = j+1; k < M; k++) {
                    temp += dp[k];
                }
                dp[j] = temp;
            }   
        }
        
        long sum = 0;
        for(int i = 1; i < M; i++) {
            sum += dp[i];
        }
        cout << sum << '\n';
    }
 
    
    return 0;
}
320x100

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

[백준] 2293 - 동전 1  (0) 2021.12.25
[백준] 2304 - 창고 다각형  (0) 2021.12.25
[백준] 11057 - 오르막 수  (0) 2021.12.25
[백준] 2812 - 크게 만들기  (0) 2021.12.23
[백준] 2841 - 외계인의 기타 연주  (0) 2021.12.23
Share Link
reply
반응형
«   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