분류
다이나믹 프로그래밍
문제 설명
스타로드와 토끼는 토르를 구출하기 위해서 우주선을 만들고 있다.
우주선을 만들기 위해서는 총 N개의 부품을 상점에서 모두 구입해야 한다. 모든 부품은 무게 W와 에너지 E를 갖고 있다. 상점에서는 모든 부품을 1부터 N번까지 부품을 순서대로 나열해놓고 판매하고 있다.
이 상점에서는 특이하게도 부품의 가격을 W*E의 비용으로 팔고 있다. 상점에서는 또한 특이한 방식의 판매를 하는데, L 부품부터 R 부품중 최대 무게 W_max 와 최대 에너지 E_max의 곱 W_max *E_max의 비용을 지불한다면 L부터 R사이의 모든 부품을 한번에 구입할 수 있다. 또한 이 상점은 X부품과 Y부품 (X<Y)을 동시에 구입하거나 X부품을 Y부품 보다 먼저 구입할 순 있지만, Y부품을 X부품보다 먼저 구입할 순 없다.
그렇다면 스타로드와 토끼가 모든 부품을 살 수 있는 최소의 비용을 구해보도록 하자.
입력
첫 번째 줄에는 부품의 개수 N(1 ≤ N ≤ 1,000)가 주어진다.
두 번째 줄에는 각 부품의 무게 W(0 ≤ W ≤ 1,000,000)가 주어진다.
세 번째 줄에는 각 부품의 에너지 E(0 ≤ E ≤ 1,000,000)가 주어진다.
출력
스타로드와 토끼가 모든 부품을 살 수 있는 최소비용을 한줄에 출력하라.
풀이1 (동적 프로그래밍 바텀 업)
시간 복잡도 O(n^3) 공간 복잡도 O(n)
아이디어: dp[n+1] 공간을 n번째 까지의 물품을 구매했을 때의 최소 비용을 저장하는 배열로 지정
특수한 방법으로 모든 물건을 한번에 구매하는 경우 vs (1~n-1)까지의 물품을 최소 비용으로 구매하는 경우 + 나머지 물품을 한번에 구매하는 경우 중 작은 값을 dp배열에 저장하게 하여 dp[n]을 구한다.
O(n)으로 할 수 없는 이유
단순하게 현재의 물건까지의 최소비용 dp[i-1] + 다음 물건을 단일 구매 비용 E*W vs 모든 물건 구매비용 MaxE*MaxW 라고 가정하여 문제에 접근 할 경우 (특수한 방법으로 한번에 구매, 단일 구매, 특수한 방법으로 한번에 구매) 하는 경우를 체크 할 수가 없기 때문에
5
10 9 1 7 6
4 2 99 4 3
해당 예제를 걸러낼 수 없게 된다. [10*4(1~2 구매) + 1*99(3 단일 구매) + 7*4(4~5 구매)]
결론은 추가적인 반복을 사용하여 이전 물품을 현재 물품과 특수한 방법으로 한번에 구매하는 경우들과 비교하여 비용을 최소값이 나오는 경우로 최신화 하여야한다.
i = 5 일 경우
(dp[1] + 9*99) vs (dp[2] + 7*99) vs (dp[3] + 7*4) vs (dp[4] + 6*3)
import java.io.*;
import java.util.*;
public class Main {
public static int sti(StringTokenizer st){
return Integer.parseInt(st.nextToken());
}
public static long stl(StringTokenizer st){
return Long.parseLong(st.nextToken());
}
public static void main(String[] args)throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
int n = Integer.parseInt(br.readLine());
long[] dp = new long[n+1];
Arrays.fill(dp,Long.MAX_VALUE);
int[] weights = new int[n+1];
int[] energies = new int[n+1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) weights[i] = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) energies[i] = Integer.parseInt(st.nextToken());
dp[0] = 0;
int maxWeight = 0; int maxEnergie=0;
for (int i = 1; i <=n ; i++) {
long historySum = Long.MAX_VALUE;
// 모든 물품을 한번에 구매하는 경우의 값을 구하기 위한 변수 선언
maxWeight = Math.max(maxWeight, weights[i]);
maxEnergie = Math.max(maxEnergie, energies[i]);
for (int j = 1; j < i; j++) {
// dp[5]를 채운다 가정하면 dp[1]~dp[4] + 나머지 물품들을 특수한 방법으로 구매하는 금액
// 을 모두 체크하여 가장 작은 값을 dp[5]에 저장
int tmpMaxWeight = 0;
int tmpMaxEnergie = 0;
// dp[j<i] + (j+1)~(i)까지의 물품을 한번에 구매하는 경우 체크 갱신
for (int k = j+1; k <=i; k++) {
tmpMaxEnergie = Math.max(tmpMaxEnergie, energies[k]);
tmpMaxWeight = Math.max(tmpMaxWeight,weights[k]);
}
historySum = Math.min(historySum,dp[j] + (long) tmpMaxEnergie *tmpMaxWeight);
}
// 모든 물품을 한번에 구매하는 경우의 비용
long specialSum = ((long) maxEnergie * maxWeight);
dp[i] = Math.min(historySum, specialSum);
}
System.out.println(dp[n]);
}
}
개선된 풀이2 (동적 프로그래밍 바텀 업)
시간 복잡도 O(n^2) 공간 복잡도 O(n)
각 부품에 대해 그 부품을 포함하는 서브셋에서의 최대 무게와 에너지를 찾아내는 방식 이전 방식과는 다르게 모든 부품을 한번에 고려하지 않고 매 반복시에 역순으로 돌아가서 n에 해당 하는 물품까지 구매했을 때 최소비용을 찾아내어 갱신하는 방식
import java.io.*;
import java.util.*;
public class Main {
public static int sti(StringTokenizer st){
return Integer.parseInt(st.nextToken());
}
public static long stl(StringTokenizer st){
return Long.parseLong(st.nextToken());
}
public static void main(String[] args)throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
int n = Integer.parseInt(br.readLine());
long[] dp = new long[n+1];
Arrays.fill(dp, Long.MAX_VALUE);
int[] weights = new int[n+1];
int[] energies = new int[n+1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) weights[i] = Integer.parseInt(st.nextToken());
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= n; i++) energies[i] = Integer.parseInt(st.nextToken());
dp[0] = 0;
for (int i = 1; i <= n; i++) {
int maxWeight = 0, maxEnergie = 0;
for (int j = i; j >= 1; j--) {
maxWeight = Math.max(maxWeight, weights[j]);
maxEnergie = Math.max(maxEnergie, energies[j]);
dp[i] = Math.min(dp[i], dp[j-1] + (long)maxWeight * maxEnergie);
}
}
System.out.println(dp[n]);
}
}
'PS > Gold' 카테고리의 다른 글
[Gold V] 전깃줄 - 2565 (Java) (0) | 2023.07.25 |
---|---|
[Gold V] 퇴사 2 - 15486 (Java) (0) | 2023.07.24 |
[Gold V] 호텔 - 1106 (Java) (0) | 2023.07.23 |
[Gold V] 동전 2 - 2294 (0) | 2023.07.20 |
[Gold II] 직각다각형 - 17611 (0) | 2023.07.19 |