Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 카카오클라우드스쿨2기
- 생활코딩
- 생활코딩 머신러닝야학
- 딥러닝
- 데이터베이스 개론
- 머신러닝
- 머신러닝(딥러닝)
- Java
- tensorflow
- pandas
- reshape
- 판다스
- 개발자
- 데이터베이서
- JavaScript
- 머신러닝야학
- 이것이 자바다
- 생활코딩 데이터베이스
- MySQL
- flatten
- 야학
- CNN
- 파이썬
- LeNet
- Python
- Database
- 데이터베이스
- 연산자
Archives
- Today
- Total
IT's 우
[java]백준 2293번: 동전 1, 동적 계획법(dynamic programming) 알고리즘 본문
728x90
https://www.acmicpc.net/problem/2293
2293번: 동전 1
첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.
www.acmicpc.net
동적 계획법(dynamic programming)
복잡한 문제를 여러 개의 간단한 문제로 분리하여 부분의 문제들을 해결함으로 써
최종적으로 복잡한 문제의 답을 구하는 방법
동적 계획법의 원리와 구현 방식
① 큰 문제를 작은 문제로 나눌 수 있어야 한다.
② 작은 문제들이 반복돼 나타나고 사용되며 이 작은 문제들의 결괏값은 항상 같아야 한다.
③ 모든 작은 문제들은 한 번만 계산해 DP 테이블에 저장하며 추후 재사용할 때는 이 DP 테이블을 이용한다. 이를 메모제이션(memoization) 기법이라고 한다.
④ 동적 계획법은 톱-다운 방식top-down과 바텀-업 bottom-up 방식으로 구현할 수 있다.
ex) 피보나치 수열 공식: D[N] = D[N-1] + D[N-2] // N번째 수열= N-1번째 수열 + N-2번째 수열
동적 계획법의 핵심 이론
1. 동적 계획법으로 풀 수 있는지 확인
2. 점화식 세우기
3. 메모제이션 원리 이해하기
4. 톱-다운 구현 방식 이해하기
5. 바텀-업 구현 방식 이해하기
-> 더 안전한 방식은 바텀-업, 톱-다운 방식은 재귀 함수의 형태로 구현돼있기 때문에 재귀의 깊이가 매우 깊어질 경우 런타임 에러가 발생할 수 있다.
코드
import java.util.*;
public class j2293 {
static ArrayList<ArrayList<Integer>> coins;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // n가지 종류의 동전
int k = sc.nextInt(); // k원
int[] coins = new int[n]; // n개의 동전의 가치
for (int i = 0; i < n; i++) {
coins[i] = sc.nextInt();
}
int[] DP = new int[k + 1]; // 0원부터 k원까지 가능한 경우의 수-> 구할 값은 value[k]
// 첫번째 코인으로 먼저 value를 구해준다. 0의 중복을 피하기 위하여, 동전을 하나씩 비교하며 경우의 수 더해주기
int first = coins[0];
for (int i = 0; i <= k; i++) {
if (i % first == 0) {
DP[i]++;
}
}
// 다음 동전부터 1~k번째 비교
for (int i = 1; i < n; i++) {
int cur = coins[i]; // 현재 비교할 코인
for (int j = 1; j <= k; j++) { // j는 1~k원을 의미한다. 1원부터 하는 이유는 0은 모두가 0인 1개인데 위 첫번째 코인때 구해줬으므로
if (j >= cur) { // 현재 코인을 넣어서 비교하려면 j원이 현재코인 cur보다 커야하므로
// value[j](j원의 경우의 수)는 전까지의 코인으로 구했던 j까지의 경우의 수(value[j])에 현재 코인 넣었을
// 때(value[j-cur) 더해주기
// value[j-cur]의 이유: 현재코인 하나 넣어줬을 때는 j원에서 현재코인의 값 빼줬을 때 경우의 수이므로
DP[j] = DP[j] + DP[j - cur];
}
}
}
System.out.println(DP[k]);
}
}
풀이
int[] DP = new int[k + 1]; // 0원부터 k원까지 가능한 경우의 수-> 구할 값은 value[k]
k원이 가능한 코인들의 경우의 수를 구하므로 DP [] // 0~k까지의 경우의 수를 구하자
예시에 1, 2, 5 가치의 코인들이 있다. 코인을 차례대로 DP []를 구해준다.
* 첫 번째 코인을 사용하여 DP []를 구한다. 0~k까지 코인으로 나누어 떨어지면(가능하므로) DP [i]++해준다.
// 첫번째 코인으로 먼저 value를 구해준다. 0의 중복을 피하기 위하여, 동전을 하나씩 비교하며 경우의 수 더해주기
int first = coins[0];
for (int i = 0; i <= k; i++) {
if (i % first == 0) {
DP[i]++;
}
}
두 번째 코인부터 n번째 코인까지 차례대로 j: 1~k원까지 DP [j]를 구해준다.
* 현재 코인 cur을 j: 1~k원까지 하나씩 넣어서 비교
// j가 1 원부터인 이유는 첫 번째 코인에서 0번째를 구해줬는데 0원이 되는 경우의 수는 모두가 0인 하나이므로 끝
// 다음 동전부터 1~k번째 비교
for (int i = 1; i < n; i++) {
int cur = coins[i]; // 현재 비교할 코인
for (int j = 1; j <= k; j++) { // j는 1~k원을 의미한다. 1원부터 하는 이유는 0은 모두가 0인 1개인데 위 첫번째 코인때 구해줬으므로
if (j >= cur) { // 현재 코인을 넣어서 비교하려면 j원이 현재코인 cur보다 커야하므로
// value[j](j원의 경우의 수)는 전까지의 코인으로 구했던 j까지의 경우의 수(value[j])에 현재 코인 넣었을
// 때(value[j-cur) 더해주기
// value[j-cur]의 이유: 현재코인 하나 넣어줬을 때는 j원에서 현재코인의 값 빼줬을 때 경우의 수이므로
DP[j] = DP[j] + DP[j - cur];
}
}
}
현재 코인: cur
j: 1~k원이 cur보다 크거나 같아야지 동전을 넣어주므로 j>=cur 조건일 때 점화식을 사용한다.
점화식: D [j]=D [j]+D [j-cur]
전까지 구했던 j원까지의 경우의 수 D [j]에 * 현재 코인 cur을 j: 1~k원까지 하나씩 넣어서 비교할 것이므로 현재 코인을 하나 넣었을 때는 j-cur원이므로 j-cur원 가치 경우의 수 D [j-cur]을 더해준다.
반복하면 끝!
출처: 백준, Do it! 알고리즘 코딩테스트 자바 편(김종관 지음)
728x90
반응형