일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 99클럽
- 트리
- 데이터베이스
- 자바
- 우선순위큐
- 알고리즘
- 백준
- lower bound
- Til
- BinarySearch
- DFS
- 그래프 이론
- 백트래킹
- 프로그래머스
- BFS
- 동적 프로그래밍
- 그리디
- 스프링 핵심 원리 - 기본편
- DP
- Spring
- Java
- 네트워크 계층
- 브루트포스
- 완전탐색
- 그래프
- 스프링
- 코딩테스트준비
- 항해99
- 개발자취업
- 정렬
- Today
- Total
AtraFelis's Develop Diary
[99클럽 코테스터디 25일차 TIL / 백준] 1351 - 무한 수열 (DP, HashMap) 본문
99클럽 코테스터디 25일차 TIL
KeyWord : DP, Map
문제
무한 수열 A는 다음과 같다.
- $A_0 = 1$
- $A_i = A_{⌊i/P⌋} + A_{⌊i/Q⌋} (i ≥ 1)$
N, P와 Q가 주어질 때, AN을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 3개의 정수 N, P, Q가 주어진다.
출력
첫째 줄에 AN을 출력한다.
제한
- $0 ≤ N ≤ 10^{12}$
- $2 ≤ P, Q ≤ 10^9$
풀이
매우 간단한 DP 문제이다. 처음부터 문제에 수열의 점화식도 주어졌으므로, 그대로 메모제이션을 하면서 코드를 구현하면 될 듯하다. (bottom-up 방식이 아닌, top-down의 재귀 방식으로 구현하면 쉽다.)
하지만 이 정도만 고려하고 곧장 코드를 구현한다면 반드시 실패할 것인데, 아래의 조건 때문이다.
- $0 ≤ N ≤ 10^{12}$
N의 범위가 32bit 정수 자료형을 초과한다. 즉, int가 아닌 long 자료형을 사용해야 한다는 것이다.
이제 통과할 수 있을까?
하지만 대부분은 메모리 초과로 실패한다. 아마 대부분의 분들은 아래와 같이 메모제이션을 하려고 했을 것이기 때문이다.
Long[] dp = new Long[N+1];
다른 DP 문제였다면 문제가 없었겠지만, 이번에도 N의 범위가 문제다.
여기서 N의 값으로 $10^{12}$이 들어온다면? $10^{12}$ 크기의 Long 배열이 만들어진다. $8bytes \times 10^{12}$라는 엄청난 양의 메모리를 차지하게 된다.
즉, 여기서 당연하게도 메모리 초과가 발생한다.
점화식을 살펴보자.
$A_i = A_{⌊i/P⌋} + A_{⌊i/Q⌋} (i ≥ 1)$
조금 더 이해하기 쉽게 코드로 표현하면 아래와 같다.
dp[i] = dp[i/P] + dp[i/Q]
여기서 $2 ≤ P, Q$ 이므로 인덱스는 매번 점화식이 진행될 때마다 절반식 줄어든다. 즉, 그냥 N 크기의 배열을 이용하면 낭비되는 공간이 매우 많다는 것을 알 수 있다.
- N = 100
- P = 2
- Q = 2
이렇게 입력이 주어졌을 때, 실제로 사용되는 인덱스는 100, 50, 25, 12, 6, 3, 1, 0이다. 위에서 설명한한 것처럼 대부분의 인덱스는 사용되지 않는다는 것을 알 수 있다.
핵심은 이렇게 공간이 낭비되지 않게끔 하는 것이다. 어떤 방법이 있을까?
이것을 해결하려면 기존의 dp 문제를 해결할 때의 습관에서 벗어나야 한다. 생각해보면 메모제이션을 할 때 반드시 배열을 사용할 필요는 없다. 그냥 이 인덱스일 때는 이 값을 가진다, 라는 것만 기록할 수 있으면 된다.
인덱스와 값. key와 value. 즉, Map 자료 구조가 떠오르지 않는가?
Map<Long, Long> dp = new HashMap<>();
이렇게 메모제이션을 위한 해쉬 맵을 선언한 후, key에는 인덱스, value에는 메모제이션을 위한 값을 저장하면 된다.
값을 꺼낼 때의 시간 복잡도 또한 $O(1)$이므로 배열을 이용하는 것과 별 차이가 없다.
이제 진짜로 코드로 구현만 하면 된다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
public class Main {
static Map<Long, Long> dp = new HashMap<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine());
Long n = Long.parseLong(stk.nextToken());
Long p = Long.parseLong(stk.nextToken());
Long q = Long.parseLong(stk.nextToken());
// N 크기의 DP 배열 선언은 0 <= N <= 10^12 조건 때문에 불가능
// HashMap<idx, value>로 해결
dp.put(0L, 1L);
// top-down 방식
solution(n, p, q);
System.out.println(dp.get(n));
}
private static void solution(Long N, Long P, Long Q) {
if(dp.containsKey(N)) {
return;
}
solution(N/P, P, Q);
solution(N/Q, P, Q);
dp.put(N, dp.get(N/P) + dp.get(N/Q));
}
}
'Algorithm > 백준' 카테고리의 다른 글
[백준 | JAVA] 27527 - 배너 걸기 (슬라이딩 윈도우) (0) | 2025.02.24 |
---|---|
[99클럽 코테스터디 24일차 TIL / 백준] 2225 - 합분해 (DP) (0) | 2025.02.20 |
[99클럽 코테스터디 23일차 TIL / 백준] 9251 - LCS (DP) (0) | 2025.02.19 |
[99클럽 코테스터디 22일차 TIL / 백준] 11053 - 가장 긴 증가하는 부분 수열(DP) (0) | 2025.02.18 |
[99클럽 코테스터디 21일차 TIL / 백준] 1003 - 피보나치 함수 (DP) (0) | 2025.02.17 |