N
: 줄의 수
내려가기 게임을 하고 있다.
N줄에 0~9 사이 숫자가 3개씩 적혀 있을 때, 얻을 수 있는 최대 점수, 최소 점수를 구하는 문제이다.
⭐️ 내려가기 게임의 제약 조건
- 첫 줄에서 시작 → 마지막 줄에서 끝나는 놀이
- 처음의 3개 숫자 중 하나를 골라 시작
- 다음 줄 내려갈 수 있는 2가지 조건
- 바로 아래의 수로 넘어가기 ⭕️
- 바로 아래의 수와 붙어 있는 수로만 이동 ⭕️
위의 규칙에 따라 마지막 줄까지 내려왔을 때 최대 점수, 최소 점수를 구해야 한다.
모든 줄에서 최댓값, 최솟값을 얻도록 한다면 마지막 줄에서도 최댓값, 최솟값을 얻을 수 있을 것이다.
→ 앞의 결과를 이용할 수 있는 DP로 해결하도록 한다!
최대, 최소를 구해야 하기 때문에 DP 테이블 2개를 정의한다.
→ 🚨 이렇게 하면 메모리 초과
가 발생하기 때문에 테이블에 따로 값을 저장하지 않고 매번 갱신하는 형태로 변경하였다.
💡 현재 위치한 줄의 3개의 칸은 제약 조건에 따라 각각 이전 줄의 다른 지점에서 올 수 있다.
- 이전 줄 1번째 칸 또는 2번째 칸 → 현재 1번째 칸
- 이전 줄 1번째 칸 또는 2번째 칸 또는 3번째 칸 → 현재 2번째 칸
- 이전 줄 2번째 칸 또는 3번째 칸 → 현재 3번째 칸
이와 같이 각각의 칸에 대해 여러 가지 경우가 존재한다.
한줄한줄 입력받을 때마다 현재 위치의 점수를 합했을 때 최대/최소가 되는 값으로 갱신해준다.
→ 마지막 줄까지 진행했을 때 최종으로 원하는 값을 구할 수 있도록 구현한다.
N개의 줄마다 3가지 숫자를 이용해 최대/최소 갱신 →
최종 시간복잡도
으로 최악의 경우 이 되어 1초 내 연산이 가능하다.
DP 테이블에 저장하지 않고 최대/최소 갱신하면서 구하기
# 각 줄의 점수 입력
scores = [list(map(int, input())) for _ in range(N)]
ValueError: invalid literal for int() with base 10: ' '
split()
를 빼먹었다.❌ 원래의 아이디어
현재 위치의 점수를 합했을 때 최대/최소가 되는 값을 DP 테이블에 넣어주면 된다!1번째 칸에 대하여 점화식을 작성하면 다음과 같다.
max_dp[i][0] = max(max_dp[i-1][0], max_dp[i-1][1]) + scores[i][0] # 최댓값
min_dp[i][0] = min(min_dp[i-1][0], min_dp[i-1][1]) + scores[i][0] # 최솟값
for문을 통해 아래와 같이 값을 채워준다.
# DP 테이블 채우기 for i in range(1, N): # 첫번째 숫자 : 이전 줄의 첫번째 또는 두번째 max_dp[i][0] = max(max_dp[i-1][0], max_dp[i-1][1]) + scores[i][0] # 두번째 숫자 : 이전 줄의 첫번째 또는 두번째 또는 세번째 max_dp[i][1] = max(max_dp[i-1][0], max_dp[i-1][1], max_dp[i-1][2]) + scores[i][1] # 세번째 숫자 : 이전 줄의 두번째 또는 세번째 max_dp[i][2] = max(max_dp[i-1][1], max_dp[i-1][2]) + scores[i][2] # 첫번째 숫자 : 이전 줄의 첫번째 또는 두번째 min_dp[i][0] = min(min_dp[i-1][0], min_dp[i-1][1]) + scores[i][0] # 두번째 숫자 : 이전 줄의 첫번째 또는 두번째 또는 세번째 min_dp[i][1] = min(min_dp[i-1][0], min_dp[i-1][1], min_dp[i-1][2]) + scores[i][1] # 세번째 숫자 : 이전 줄의 두번째 또는 세번째 min_dp[i][2] = min(min_dp[i-1][1], min_dp[i-1][2]) + scores[i][2]
위와 같이 채워진 2개의 DP 테이블에서 마지막 값인 인덱스
N-1
의 3가지 값 중 최대/최소의 값을 출력하면 원하는 답을 얻을 수 있다.
메모리 초과
가 발생했다.32 23
으로 잘못 나왔다. # 최솟값 갱신
# 첫번째 숫자 : 이전 줄의 첫번째 또는 두번째
min_dp[0] = min(min_dp[0], min_dp[1]) + scores[0]
# 두번째 숫자 : 이전 줄의 첫번째 또는 두번째 또는 세번째
min_dp[1] = min(min_dp[0], min_dp[1], min_dp[2]) + scores[1]
# 세번째 숫자 : 이전 줄의 두번째 또는 세번째
min_dp[2] = min(min_dp[1], min_dp[2]) + scores[2]
import sys
input = sys.stdin.readline
# N 입력
N = int(input())
# 첫번째 입력 받기
scores = list(map(int, input().split()))
# 초기값 설정
max_dp = scores
min_dp = scores
# 최대, 최소 갱신하기
for _ in range(N-1):
# 이후 점수 입력 받기
scores = list(map(int, input().split()))
# 최댓값 갱신
max_dp = [max(max_dp[0], max_dp[1]) + scores[0], max(max_dp[0], max_dp[1], max_dp[2]) + scores[1], max(max_dp[1], max_dp[2]) + scores[2]]
# 최솟값 갱신
min_dp = [min(min_dp[0], min_dp[1]) + scores[0], min(min_dp[0], min_dp[1], min_dp[2]) + scores[1], min(min_dp[1], min_dp[2]) + scores[2]]
# 결과 출력
print(max(max_dp), min(min_dp))