2024. 3. 21. 11:38ㆍ코딩 테스트/백준
https://www.acmicpc.net/problem/5557
5557번: 1학년
상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀
www.acmicpc.net
전체 코드
n = int(input())
dp = [[0 for _ in range(21)] for _ in range(n)]
# dp[i][j] -> i번째 숫자까지 고려했을 때, 합이 j가 되는 경우의 수
arr = list(map(int, input().split()))
# 첫번째 수 저장
dp[0][arr[0]] = 1
# 마지막 숫자를 제외하고 순회
for i in range(1, n - 1):
for j in range(21): # 모든 경우의 수에 대해
if dp[i - 1][j]: # 이전 단계에서 j값을 만들 수 있는 경우의 수가 있다면
# 숫자를 더했을떄 범위 내에 있다면
if 0 <= j + arr[i] <= 20:
dp[i][j + arr[i]] += dp[i - 1][j]
# 숫자를 뺐을때 범위 내에 있다면
if 0 <= j - arr[i] <= 20:
dp[i][j - arr[i]] += dp[i - 1][j]
# 입력받았던 숫자 중 마지막 숫자와 일치하는 경우의 수가 얼마인지 출력
print(dp[n - 2][arr[-1]])
풀이
다음과 같은 DP 테이블을 생성한다. 숫자의 범위는 0~20이며 이를 column으로 설정한다. 제공되는 숫자 n 길이를 기준으로 row를 설정한다.
첫 row의 column은 arr[0]을 인덱스로 하며 1로 초기화한다.
row 인덱스는 1~n-1 범위로 순회한다. col은 0~20 범위로 순회를 진행한다. 순회 중 이전 row에 값이 존재하는 경우 0 =< j + arr[i] <= 20 범위라면 dp[i][j+arr[i]], dp[i][j-arr[i]] 위치에 해당 값을 더한다.
만약 범위에서 벗어나면 dp 테이블에 기록되지 않는다.
동일한 부분이 만나게 되면 해당 경우의 수가 동시에 존재했다는 의미이다. 이 경우 숫자를 더해준다.
이렇게 쭉 진행하면 0~20까지 더하기, 빼기 중 발생되는 숫자가 누적된다. 마지막 숫자 arr[-1]와 일치하는 숫자의 개수는 dp[n-2][arr[-1]]을 통해 접근하여 가져올 수 있다.
회고
DP 테이블을 사용하여 접근한다는 생각 자체를 못했다. 0-1 배낭문제처럼 테이블을 순차적으로 타뷸레이션을 진행하는데 바로 떠올리기 쉽지 않았다.
이 문제와 0-1 배낭 문제의 공통점이라면 '모든 경우의 수'를 구한다는 점이고, 숫자의 범위가 주어진다는 점이다. 다이나믹으로 푸는 문제임을 확인했다면 arr의 인덱스를 행으로 만들고, 숫자 범위를 열로 생성한 후 순차적으로 누적시켜 계산하는 것으로 접근해야한다.
'코딩 테스트 > 백준' 카테고리의 다른 글
백준 알고리즘 2644. 촌수계산 (0) | 2024.03.22 |
---|---|
백준 알고리즘 2294. 동전 2 (0) | 2024.03.22 |
백준 알고리즘 1309. 동물원 (0) | 2024.03.21 |
백준 알고리즘 1541. 잃어버린 괄호 (0) | 2024.03.21 |
백준 알고리즘 1743. 음식물 피하기 (0) | 2024.03.20 |