[문제 위치]
https://www.acmicpc.net/problem/22236
[문제 풀이]
이 문제는 DP를 이용해 조건을 만족하는 비행 경로의 개수를 세는 문제이다.
문제가 요구하는 것은 총 거리 d를 이동하는 동안, 매 순간 고도가 정확히 1씩만 변하고(±1), 출발과 도착을 제외하고는 고도 0에 닿지 않는 경로의 개수(모듈러 m)이다.
중간에 고도 0이 되는 순간은 “착륙”이므로 그 경로는 불가능하다.
이를 위해 dp[i][h]를 다음처럼 정의한다.
- dp[i][h] = “총 i번 이동했을 때(=거리 i), 고도가 h이고, 도착 전까지는 고도 0에 한 번도 닿지 않은 경로의 수”
이제 전이를 생각해보자.
한 번 이동할 때 고도는 반드시 ±1만큼 변한다.
즉, i번째에 고도 h에 도착하려면, 바로 이전(i-1)에는
- h-1에 있다가 올라오거나
- h+1에 있다가 내려와야 한다.
따라서 점화식은 다음과 같다.
- dp[i][h] = dp[i-1][h-1] + dp[i-1][h+1] (mod m)
여기서 중요한 제약이 하나 더 있다.
- 도착(i = d) 전에는 고도 0이면 안 되므로, i < d인 동안은 h = 0 상태를 만들지 않는다.
- 구현에서는 아예 h ≥ 1만 채우고, dp[*][0]은 0으로 두면 된다.
처음 고도는 0인데, 중간에 0이면 안 되므로 첫 이동은 무조건 상승밖에 없다.
- dp[1][1] = 1
마지막(d번째)에는 반드시 고도 0으로 착륙해야 한다.
고도 변화가 ±1이므로, 마지막 직전(d-1) 고도는 반드시 1이어야만 0으로 내려올 수 있다.
- 정답 = dp[d-1][1] (mod m)
점화식이 항상 더 작은 i-1만 참조하므로 dp[1]부터 dp[d-1]까지 바텀업으로 채우면 된다.
또한 한 행을 계산할 때 직전 행만 필요하므로, 2개의 1차원 배열로 굴려서 메모리를 O(d)로 줄일 수 있다.
아래는 이를 구현한 코드이다.
#include <iostream>
#include <vector>
using namespace std;
#define FASTIO ios::sync_with_stdio(false); cin.tie(nullptr);
int main() {
FASTIO
int d;
long long m;
cin >> d >> m;
// d가 홀수면 0으로 돌아올 수 없음 (문제에선 d가 짝수로 주어지는 편)
if (d < 2 || (d & 1)) {
cout << 0 << '\n';
return 0;
}
vector<long long> prev(d + 3, 0), cur(d + 3, 0);
// 첫 이동: 고도 0 -> 1 (중간에 0이면 안 되므로 시작은 항상 상승)
prev[1] = 1; // i=1, h=1
// i = 2 .. d-1 (도착 전까지는 고도 0 금지 => h는 1 이상만 유지)
for (int i = 2; i <= d - 1; i++) {
fill(cur.begin(), cur.end(), 0);
for (int h = 1; h <= i; h++) {
long long val = prev[h - 1] + prev[h + 1];
cur[h] = val % m;
}
prev.swap(cur);
}
// 마지막 한 칸은 (d-1, 1) -> (d, 0) 착륙만 가능
cout << (prev[1] % m) << '\n';
return 0;
}'문제 풀이 > 문제 풀이(BOJ)' 카테고리의 다른 글
| [Silver V] 정말 좋은 압축 - 5043 (0) | 2026.01.25 |
|---|---|
| [Silver IV] 피보나치 수 7 - 15624 (1) | 2026.01.20 |
| [Gold IV] 주사위 게임 - 13250 (0) | 2026.01.19 |
| [Gold IV] 저금통 - 2421 (0) | 2026.01.15 |
| [Gold IV] 축구 - 1344 (0) | 2026.01.13 |