문제 링크
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번째 줄까지 정수 삼각형이 주어진다.
- 출력 : 첫째 줄에 합이 최대가 되는 경로에 있는 수의 합을 출력한다.
| 예제 입력 | 예제 출력 |
| 5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 |
30 |
강의 영상
중복되는 연산을 줄이려면? "기억하며 풀기!"
DP는 메모리를 사용해서 중복 연산을 줄이고, 중복연산을 줄여서 수행 속도를 개선하는 방법이다.
- 메모리를 사용한다는 것은 배열 또는 다른 자료구조를 사용함을 의미한다.
- 중복연산을 줄인다는 것은 연산한 결과를 배열에 담는다는 것을 의미한다.
DP 사용 판단 기준
- DFS로 풀 수 있지만 경우의 수가 너무 많은 문제
- 직접 손으로 계산하면서 규칙 찾기
- 패턴이 보이면 수학적 식 도출하기
- 경우의 수들에 중복되는 연산이 많은 경우
문제 해결 접근 방법
- 최대한 많은 DP 문제들을 풀어보고, 풀이들을 참고하면서 DP식 사고방식 습득
- 스택이나 큐처럼 정형화된 알고리즘이 아니기 때문에, 한 문제를 오래 붙들기 보다는 풀이를 보면서 구현법을 익히는 것이 더 좋을 수도 있다.
- 어떻게 자료구조를 만들어서 어떤 정보를 담아야 이전 단계로 돌아가지 않을까?
문제 분석
이 문제는 DFS를 사용해서도 풀 수 있지만, 삼각형의 크기가 최대 500이기 때문에 시간 초과가 발생할 것이다. 이는 이미 계산했던 숫자들을 중복으로 계산하기 때문에 발생하는 문제이다. 따라서 이 문제의 관건은 어떻게 해야 중복되는 연산을 최소화할 수 있냐는 것이다.

원본 삼각형을 보면, 하나의 숫자에 대해 더하기 연산은 ↓ 또는 ↘ 방향으로 이루어 진다. 중복되는 덧셈 연산을 방지하기 위해 동적 계획법을 사용해야 한다. 이제 최대값을 저장할 배열인 dp 배열을 만들자.

첫 번째 줄에 있는 숫자는 7 밖에 없으므로 더하기 연산 없이 바로 종료된다.

두 번째 줄은 2개의 숫자가 있다. 7+3=10과 7+8=15가 None보다 크므로 각각 업데이트해준다.

세 번째 줄에는 3개의 숫자가 있다.
- 우선 첫번째 숫자에 대해 dp[2][0] + ori[3][0] = 10 + 8 = 18이 None보다 크므로 dp[3][0]을 18로 업데이트한다.
- 그 다음 연산으로 dp[2][0] + ori[3][1] = 10 + 1 = 11이 None보다 크므로 dp[3][1]을 11로 업데이트한다.
- 두 번째 숫자에 대해 dp[2][1] + ori[3][1] = 15 + 1 = 16이 11보다 크므로 dp[3][1]을 16으로 업데이트한다.
- 그 다음 연산으로 dp[2][1] + ori[3][2] = 15 + 0 = 15가 None보다 크므로 dp[3][2]를 15로 업데이트한다.

같은 논리로 마지막줄까지 업데이트하면 마지막 행은 [24, 30, 27, 26, 24]가 된다. 여기서의 최대값 30이 이 문제의 정답이 된다.
코드
import sys
input = sys.stdin.readline
n = int(input())
arr = [list(map(int, input().split())) for _ in range(n)]
dp = [[0] * n for _ in range(n)]
dp[0][0] = arr[0][0]
for i in range(n-1):
for j in range(i+1):
if dp[i+1][j] < dp[i][j] + arr[i+1][j]:
dp[i+1][j] = dp[i][j] + arr[i+1][j]
if dp[i+1][j+1] < dp[i][j] + arr[i+1][j+1]:
dp[i+1][j+1] = dp[i][j] + arr[i+1][j+1]
print(max(dp[-1]))'PS > 백준' 카테고리의 다른 글
| [백준] (9252) LCS 2 [Python] (0) | 2025.04.07 |
|---|---|
| [백준] (9251) LCS [Python] (0) | 2025.04.07 |
| [백준] (2252) 줄 세우기 [Python] (0) | 2025.04.01 |
| [백준] (1753) 최단경로 [Python] (0) | 2025.03.31 |
| [백준] (1300) K번째 수 [Python] (0) | 2025.03.30 |