View

300x250

문제링크

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

문제

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5

위 그림은 크기가 5인 정수 삼각형의 한 모습이다.

맨 위층 7부터 시작해서 아래에 있는 수 중 하나를 선택하여 아래층으로 내려올 때, 이제까지 선택된 수의 합이 최대가 되는 경로를 구하는 프로그램을 작성하라. 아래층에 있는 수는 현재 층에서 선택된 수의 대각선 왼쪽 또는 대각선 오른쪽에 있는 것 중에서만 선택할 수 있다.

삼각형의 크기는 1 이상 500 이하이다. 삼각형을 이루고 있는 각 수는 모두 정수이며, 범위는 0 이상 9999 이하이다.

입력

첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.

출력

첫째 줄에 합이 최대가 되는 경로에 있는 수의 합을 출력한다.

조건

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

해설

각 숫자에 0부터 순서대로 번호를 매겨서 맨 위에서 아래로 내려오면서 값을 더해 최대 합을 구해나간다. 가장 아래에 도착하면, 구한 합들을 비교하여 그 중 가장 큰 값을 선택하여 출력한다. 여기에서 DP를 사용하면 쉽게 문제를 해결할 수 있다.

풀이

우선 삼각형의 숫자들에 번호를 붙여보자

0 : 0
1 : 1 2
2 : 3 4 5
3 : 6 7 8 9
4 : 10 11 12 13 14
5 : 15 16 17 18 19 20
. : ...

편의상 왼쪽의 수를 층이라고 부르자. 각 층의 첫번째 숫자들은 자연수의 누적합과 같은 모양을 보인다. ( 1, 1+2, 1+2+3, 1+2+3+4, ... ) 각 층 안에서 숫자의 증가는 누적 합에 0부터 시작하여 층 수까지 더한 값과 같다. (3층 → 6+0, 6+1, 6+2, 6+3) 이를 이용해 각 숫자의 위치를 나타내는 식은 i*(i+1)/2 + j로 볼 수 있다. 여기에서 i는 층, j는 층 내에서 증가하는 숫자이다. 모든 위치를 정확히 명시하며 값을 입력받는 방법은 아래와 같다.

for(int i = 0; i < n; i++) {
    for(int j = 0; j <= i; j++) {
        cin >> arr[i*(i+1)/2 + j];
    }
}

2층까지 최댓값으로 선택을 완료했다고 할 때, 3층의 7에 접근할 수 있는 위치는 3, 4이다. 3층까지 선택을 한 뒤 4층의 11은 6과 7에서 접근할 수 있다. 이를 수식적으로 말하자면, i*(i+1)/2 + j에 접근할 수 있는 위치는 (i-1)*i/2 + j-1(i-1)*i/2 + j이다. 둘 중 큰 값을 가져와 해당 위치의 값을 더해주면 그 위치까지의 선택할 수 있는 최댓값이 된다. 0층에 대해서는 index가 음수인 값에 접근하게 되므로, arr[0]은 제외해주었다.

// 초기값 대입
    cin >> arr[0];
    
    for(int i = 1; i < n; i++) {
        for(int j = 0; j <= i; j++) {
            cin >> arr[i*(i+1)/2 + j];

            arr[i*(i+1)/2 + j] += max(arr[(i-1)*i/2 + j], arr[(i-1)*i/2 + j-1]);
        }
    }

그러나, 위와 같은 방식으로 하면 각 층의 가장 왼쪽, 가장 오른쪽에 있는 위치에서는 잘못된 값을 가져오게 된다. (3층의 가장 왼쪽인 6번은 2층의 3번과 1층의 2번에 접근) 그러므로, 이를 정상적으로 처리하기 위해서 j가 0이거나 i인 경우에는 예외처리를 해주어야 한다.

// 초기값 대입
cin >> arr[0];

for(int i = 1; i < n; i++) {
    for(int j = 0; j <= i; j++) {
        cin >> arr[i*(i+1)/2 + j];
        if(j==0) {
            arr[i*(i+1)/2 + j] += arr[(i-1)*i/2 + j];
        } else if (j==i){
            arr[i*(i+1)/2 + j] += arr[(i-1)*i/2 + j-1];
        } else {
            arr[i*(i+1)/2 + j] += max(arr[(i-1)*i/2 + j], arr[(i-1)*i/2 + j-1]);
        }
    }
}

위 코드를 거치고 나면 가장 아랫층에는 각 선택지에 대한 최댓값들이 저장되어있다. n번째 층을 돌면서 최댓값을 찾아 출력해주면 원하는 결과를 얻을 수 있다.

int max = 0;
for(int j = 0; j < n; j++) {
    if(arr[n*(n-1)/2 + j] > max) max = arr[n*(n-1)/2+j];
}

cout << max;


코멘트

이정도는 쉽지. 후후후. 코드도 읽기 쉽게 나름 깔끔하게 짠 것 같아.


코드

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main () {
    int n;
    cin >> n;
    vector<int> arr(n*(n+1)/2);
    
    // 초기값 대입
    cin >> arr[0];
    
    for(int i = 1; i < n; i++) {
        for(int j = 0; j <= i; j++) {
            cin >> arr[i*(i+1)/2 + j];
            if(j==0) {
                arr[i*(i+1)/2 + j] += arr[(i-1)*i/2 + j];
            } else if (j==i){
                arr[i*(i+1)/2 + j] += arr[(i-1)*i/2 + j-1];
            } else {
                arr[i*(i+1)/2 + j] += max(arr[(i-1)*i/2 + j], arr[(i-1)*i/2 + j-1]);
            }
        }
    }
    
    int max = 0;
    for(int j = 0; j < n; j++) {
        if(arr[n*(n-1)/2 + j] > max) max = arr[n*(n-1)/2+j];
    }
    
    cout << max;
    
    return 0;
}
320x100

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

[백준] 1003 - 피보나치 함수  (2) 2021.08.09
[백준] 1149 - RGB거리  (0) 2021.08.09
[백준] 11726 - 2xn 타일링  (0) 2021.08.09
[백준] 9095 - 1, 2, 3 더하기  (0) 2021.08.09
[백준] 8983 - 사냥꾼  (1) 2021.08.09
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