분류
다이나믹 프로그래밍
문제 설명
상담원으로 일하고 있는 백준이는 퇴사를 하려고 한다.
오늘부터 N+1일째 되는 날 퇴사를 하기 위해서, 남은 N일 동안 최대한 많은 상담을 하려고 한다.
백준이는 비서에게 최대한 많은 상담을 잡으라고 부탁을 했고, 비서는 하루에 하나씩 서로 다른 사람의 상담을 잡아놓았다.
각각의 상담은 상담을 완료하는데 걸리는 기간 Ti와 상담을 했을 때 받을 수 있는 금액 Pi로 이루어져 있다.
N = 7인 경우에 다음과 같은 상담 일정표를 보자.
| 1일 | 2일 | 3일 | 4일 | 5일 | 6일 | 7일 | |
|---|---|---|---|---|---|---|---|
| Ti | 3 | 5 | 1 | 1 | 2 | 4 | 2 |
| Pi | 10 | 20 | 10 | 20 | 15 | 40 | 200 |
1일에 잡혀있는 상담은 총 3일이 걸리며, 상담했을 때 받을 수 있는 금액은 10이다. 5일에 잡혀있는 상담은 총 2일이 걸리며, 받을 수 있는 금액은 15이다.
상담을 하는데 필요한 기간은 1일보다 클 수 있기 때문에, 모든 상담을 할 수는 없다. 예를 들어서 1일에 상담을 하게 되면, 2일, 3일에 있는 상담은 할 수 없게 된다. 2일에 있는 상담을 하게 되면, 3, 4, 5, 6일에 잡혀있는 상담은 할 수 없다.
또한, N+1일째에는 회사에 없기 때문에, 6, 7일에 있는 상담을 할 수 없다.
퇴사 전에 할 수 있는 상담의 최대 이익은 1일, 4일, 5일에 있는 상담을 하는 것이며, 이때의 이익은 10+20+15=45이다.
상담을 적절히 했을 때, 백준이가 얻을 수 있는 최대 수익을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N (1 ≤ N ≤ 1,500,000)이 주어진다.
둘째 줄부터 N개의 줄에 Ti와 Pi가 공백으로 구분되어서 주어지며, 1일부터 N일까지 순서대로 주어진다. (1 ≤ Ti ≤ 50, 1 ≤ Pi ≤ 1,000)
출력
첫째 줄에 백준이가 얻을 수 있는 최대 이익을 출력한다.
풀이 (동적 프로그래밍 실패)
시간 복잡도 O(n*m) 공간 복잡도 O(n)
아이디어: n번째 요일날 그날 일해서 버는 돈을 포함하지 않고 그 전날까지 일한 금액의 최고 합계를 저장하는 dp[] 공간을 잡는다.
최종적으로는 n+1의 인덱스 값을 출력한다. 일해야 하는 시간을 days, 그에 받는 돈을 money 라고 할 때
dp[i+days] = Math.max(dp[i+days], dp[i]+money) 관계식이 세워진다.
단 여기서 몇 가지 조건이 추가된다면 위에 작업은 특정 일자에만 할 수 있다는 점 과
dp[i]일날 벌 수 있는 최대의 금액이 i 보다 뒤에 존재하는 인덱스의 dp값 보다 크다면 dp[i] 값으로 최신화 할 필요가 있다는 점이다.
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+2];
int[][] info = new int[n+1][2];
for (int i = 1; i <= n; i++) {
st = new StringTokenizer(br.readLine());
int days = Integer.parseInt(st.nextToken());
int money = Integer.parseInt(st.nextToken());
info[i] = new int[]{days,money};
}
for (int i = 1; i <= n; i++) {
int days = info[i][0];
int money = info[i][1];
if(i+days<=n+1){
for (int j = i+days; j <=n+1; j++) dp[j] = Math.max(dp[i+days],dp[i]+money);
}
}
System.out.println(dp[n+1]);
}
}
풀이 (동적 프로그래밍 성공)
시간 복잡도 O(n) 공간 복잡도 O(n)
아이디어: 위에 방식과 동일 하지만 불필요한 반복을 줄이기 위해 dp[i] = Math.max(dp[i],dp[i-1]);
어재의 금액이 오늘의 금액보다 크다면 최신화 하는 코드를 추가하고
체크되지 않는 마지막 dp[n+1]의 경우도 따로 비교 함으로써 시간 복잡도를 개선하여 통과하였다.
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());
// n번째 요일날 그날 일해서 버는 돈을 포함하지 않고 그 전날까지 일한 금액의 최고 합계를 저장하는 dp
long[] dp = new long[n+2];
int[][] info = new int[n+1][2];
for (int i = 1; i <= n; i++) {
st = new StringTokenizer(br.readLine());
int days = Integer.parseInt(st.nextToken());
int money = Integer.parseInt(st.nextToken());
info[i] = new int[]{days,money};
}
for (int i = 1; i <= n; i++) {
// i+days 번째 요일 날 벌 수 있는 최고 금액의 합계가 그 전 값보다 낮은 경우 최신화
dp[i] = Math.max(dp[i],dp[i-1]);
int days = info[i][0];
int money = info[i][1];
if(i+days<=n+1) dp[i+days] = Math.max(dp[i+days],dp[i]+money);
}
long ans = Math.max(dp[n],dp[n+1]);
System.out.println(ans);
}
}'PS > Gold' 카테고리의 다른 글
| [Gold V] 암호코드 - 2011 (Java) (0) | 2023.07.27 |
|---|---|
| [Gold V] 전깃줄 - 2565 (Java) (0) | 2023.07.25 |
| [Gold V] 호텔 - 1106 (Java) (0) | 2023.07.23 |
| [Gold III] 우주선 만들기 - 15912 (Java) (0) | 2023.07.22 |
| [Gold V] 동전 2 - 2294 (0) | 2023.07.20 |