티스토리 뷰
문제
요즘 민규네 동네에서는 스타트링크에서 만든 PS카드를 모으는 것이 유행이다.
PS카드는 PS(Problem Solving)분야에서 유명한 사람들의 아이디와 얼굴이 적혀있는 카드이다. 각각의 카드에는 등급을 나타내는 색이 칠해져 있고, 다음과 같이 8가지가 있다.
- 전설카드
- 레드카드
- 오렌지카드
- 퍼플카드
- 블루카드
- 청록카드
- 그린카드
- 그레이카드
카드는 카드팩의 형태로만 구매할 수 있고, 카드팩의 종류는 카드 1개가 포함된 카드팩, 카드 2개가 포함된 카드팩, ... 카드 N개가 포함된 카드팩과 같이 총 N가지가 존재한다.
민규는 지난주에 너무 많은 돈을 써 버렸다. 그래서 오늘은 돈을 최소로 지불해서 카드 N개를 구매하려고 한다. 카드가 i개 포함된 카드팩의 가격은 Pi원이다.
예를 들어, 카드팩이 총 4가지 종류가 있고, P1 = 1, P2 = 5, P3 = 6, P4 = 7인 경우에 민규가 카드 4개를 갖기 위해 지불해야 하는 금액의 최솟값은 4원이다. 1개 들어있는 카드팩을 4번 사면 된다.
P1 = 5, P2 = 2, P3 = 8, P4 = 10인 경우에는 카드가 2개 들어있는 카드팩을 2번 사면 4원이고, 이 경우가 민규가 지불해야 하는 금액의 최솟값이다.
카드 팩의 가격이 주어졌을 때, N개의 카드를 구매하기 위해 민규가 지불해야 하는 금액의 최솟값을 구하는 프로그램을 작성하시오. N개보다 많은 개수의 카드를 산 다음, 나머지 카드를 버려서 N개를 만드는 것은 불가능하다. 즉, 구매한 카드팩에 포함되어 있는 카드 개수의 합은 N과 같아야 한다.
입력
첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000)
둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)
출력
첫째 줄에 민규가 카드 N개를 갖기 위해 지불해야 하는 금액의 최솟값을 출력한다.

풀이
큰 문제를 작은 단위의 문제로 생각해서 푸는 DP(다이나믹 프로그래밍) 알고리즘이다.
DP를 풀때 일반항 형태로 정의하는 것이 중요하다.케이스 단위로 생각해볼때, 카드 i개를 구매하는 방법은?- 카드 1개가 들어있는 카드팩을 구매하고, 카드 i-1개 구입.- 카드 2개가 들어있는 카드팩을 구매하고, 카드 i-2개 구입.- 카드 3개가 들어있는 카드팩을 구매하고, 카드 i-3개 구입....일반화 시키면 D[i] = P[n] + D[i-n] (n=1,2,3..., P[n]은 가격)
for (int i = 1; i <=n; i++) {
for (int j = 1; j <=i; j++) {
D[i] = Math.min(D[i], price[j] + D[i-j]);
}
}
만약 카드를 3개 구매하는 경우,
˙카드 1개 들어있는 카드팩 구매 + 카드 2개 구입 : price[1] + dp[2] = 19
˙카드 2개 들어있는 카드팩 구매 + 카드 1개 구입 : price[2] + dp[1] = 19
˙카드 3개 들어있는 카드팩 구매 + 카드 0개 구입 : price[3] + dp[0] = 8 (min) = dp[3]
소스코드
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
public class BOJ_S1_16194_카드구매하기2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt(); // 카드의 개수
int[] price = new int[N+1]; // 가격 저장할 배열
int[] dp = new int[N+1];
for (int i = 1; i <= N; i++) {
price[i] = sc.nextInt();
dp[i] = Integer.MAX_VALUE; // 최소값을 초기화.
}
// 카드 1개부터 N개까지. (Bottom-Up 방식)
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= i; j++) { // 카드 i개 구매할때
dp[i] = Math.min(dp[i], price[j] + dp[i-j]); // 카드 j개 카드팩 구매 + 카드 i-j개 구입할때 최소값
}
}
System.out.println(dp[N]);
}
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준/Java] 20300번: 서강근육맨 (0) | 2022.06.30 |
---|---|
[JAVA/백준] 1764번: 듣보잡(구현, 정렬) - HashSet (0) | 2022.05.19 |
[백준] 2178번: 미로탐색 (Java) / BFS, 그래프 (0) | 2022.02.23 |
[백준] 1780번: 종이의 개수 (Java) / 분할정복 (0) | 2022.02.19 |
[백준] 15683번: 감시 (Java) / DFS (0) | 2022.02.19 |
- Total
- Today
- Yesterday