1. 문제 정보
- 문제 번호: 9095번
- 문제 이름: 1, 2, 3 더하기
- 문제 요약: 정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하라.
2. 고민의 흔적: 재귀에서 DP로
처음 이 문제를 보면 "모든 경우를 다 만들어봐야 하나?"라는 생각에 재귀 함수를 떠올리기 쉽다. 하지만 숫자가 커질수록 경우의 수가 기하급수적으로 늘어나기 때문에 중복 계산이 발생하고 효율성이 떨어진다.
여기서 필요한 사고의 전환이 바로 DP(다이내믹 프로그래밍)이다.
왜 이 문제가 DP인가? (중요 포인트!)
공부하면서 정리한 DP의 핵심 판단 기준 세 가지가 이 문제에 완벽히 들어맞는다.
- 큰 문제를 작은 문제로 나눌 수 있다: n을 만드는 방법은 n-1, n-2, n-3을 만드는 방법들을 합친 것과 같다.
- 작은 문제의 결과를 저장해두면 큰 문제를 쉽게 풀 수 있다: dp[1], dp[2], dp[3]의 값을 알면 dp[4]를 바로 구할 수 있다.
- 중복 계산이 많아진다: 재귀로 풀면 같은 값을 구하는 함수가 반복적으로 호출되지만, DP는 한 번 구한 값을 배열에 저장(Memoization/Tabulation)해 재사용한다.
3. 점화식 유도 (Logic)
숫자 4를 만드는 경우를 생각해보자. 4를 만드는 마지막 단계는 세 가지 중 하나이다.
- 3을 만드는 방법에 1을 더하기: dp[3]의 모든 경우 뒤에 +1
- 2를 만드는 방법에 2를 더하기: dp[2]의 모든 경우 뒤에 +2
- 1을 만드는 방법에 3을 더하기: dp[1]의 모든 경우 뒤에 +3
따라서 점화식은 다음과 같다.
dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
초기값(Base Case) 설정
- 1: (1) → 1개 (dp[1]=1)
- 2: (1+1, 2) → 2개 (dp[2]=2)
- 3: (1+1+1, 1+2, 2+1, 3) → 4개 (dp[3]=4)
4. 코드 구현
사용자가 제출한 코드는 상향식(Bottom-Up) 방식을 사용하고 있으며, 문제의 범위가 n < 11로 매우 작기 때문에 미리 배열을 다 채워놓고 출력하는 효율적인 방식을 택했다.
#include <iostream>
using namespace std;
int main() {
// 입출력 속도 향상
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
int dp[11];
// 초기값 설정
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
// 점화식을 이용해 4부터 10까지 미리 계산 (Tabulation)
for(int i = 4; i < 11; i++){
dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
}
cin >> n;
// 테스트 케이스 수만큼 결과 출력
for(int i = 0; i < n; i++){
int number;
cin >> number;
cout << dp[number] << "\n";
}
return 0;
}
5. 배운 점 & 회고 (TIL) ✍️
① 규칙을 찾기 힘들 땐 직접 써보자
DP 문제의 점화식이 한눈에 보이지 않을 때는 n=1, 2, 3, 4, 5 정도까지 직접 손으로 써보는 것이 큰 도움이 된다. 나열하다 보면 피보나치 수열과 유사한 규칙성이 보이기 시작한다.
② "마지막 단계"를 생각하자
점화식을 세울 때 가장 강력한 팁은 "목표 지점에 도달하기 직전의 상태가 무엇인가?"를 고민하는 것이다. 1, 2, 3만 더할 수 있다면 직전 단계는 무조건 i-1, i-2, i-3일 수밖에 없다는 논리가 이 문제의 핵심이다.
③ DP의 강력함
재귀로 풀었다면 헷갈렸을 로직이 배열을 채우는 단순 반복문으로 해결되는 것을 보며, 왜 복잡한 문제일수록 자료구조와 알고리즘 선택이 중요한지 다시금 깨달았다.
'알고리즘 문제풀이 > C++' 카테고리의 다른 글
| [백준/C++] 1927번: 최소 힙 - priority_queue의 내부 구조 파헤치기 (0) | 2026.03.10 |
|---|---|
| [백준/C++] 1012번: 유기농 배추 - 격자 위에서 BFS 덩어리 찾기 (0) | 2026.03.10 |
| [백준/C++] 2839번: 설탕 배달 - 브루트 포스에서 DP로 나아가기 (0) | 2026.02.28 |
| [백준/C++] 11723번: 집합 - 비트마스킹을 활용한 최적화 (0) | 2026.02.22 |
| [백준/C++] 1764번: 듣보잡 - set_intersection과 inserter 활용하기 (0) | 2026.02.22 |