Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 1932- 정수 삼각형

sm_amoled 2021. 8. 9. 13:20

문제링크

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