백준이가 N+1일째 되는 날 퇴사하기 위해
남은 N일 동안 최대한 많은 상담을 함
각 날짜마다 상담에 소요되는 일 수와 받을 수 있는 금액이 다름
이 때, 백준이가 얻을 수 있는 최대 수익?
// [input]
7 // N (N+1일까지 상담 가능)
3 10 // 1일: 3일 소요 & 10의 이익
5 20
1 10
1 20
2 15
4 40
2 200
dfs(currentDay, currentDay)
// base-case
if currentDay > N: return sumTilNow
1) 상담 선택:
if currentDay + Ti[currentDay] <= N+1:
dfs(currentDay + Ti[currentDay], sumTilNow + Pi[currentDay])
2) 상담 미선택:
dfs(currentDay + 1, sumTilNow)
dp[i] = max(dp[i+1], Pi[i] + dp[i+Ti[i]])반복문 & dp 테이블 이용
// 1. dp 테이블 정의
// 문제에서 N이 주어짐
// But 근무 가능한 날짜: 1일 ~ N+1일 => 1 ~ N+1 인덱스 필요
int[] dp = new int[N+2];
// 2. 점화식 채우기 (거꾸로)
for(int i=N; i>=1; i--) {
// 상담 가능
if(i+Ti[i]<=N+1) {
dp[i] = Math.max(Pi[i]+dp[i+Ti[i]],dp[i+1]);
}
// 상담 불가능
else {
dp[i] = dp[i+1];
}
}
// 3. 결과 반환
public class bj14501_퇴사_DP_BottomUp {
static int Max = 0;
static int N; // 앞으로 근무가 가능한 일 수
static int[] Ti; // i번째날 상담을 했을 때 걸리는 일 수
static int[] Pi; //i번째날 상담을 했을 때 받을 수 있는 금액
static int[] dp;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// [input]
N = Integer.parseInt(br.readLine());
Ti = new int[N+1]; // 상담 완료 시간을 저장하는 배열, 1부터 시작
Pi = new int[N+1]; // 받을 수 있는 금액 저장하는 배열
dp = new int[N+2]; // 1일 ~ N+1일까지 상담 가능
for(int i=1; i<=N; i++) {
String[] temp = br.readLine().split(" ");
Ti[i] = Integer.parseInt(temp[0]);
Pi[i] = Integer.parseInt(temp[1]);
}
// [logic]
// 점화식 채우기
// dp[i]:i일부터 퇴사일까지 벌 수 있는 최대 수익
for(int i=N; i>=1; i--) {
// 상담 가능
if(i+Ti[i]<=N+1) {
dp[i] = Math.max(Pi[i]+dp[i+Ti[i]],dp[i+1]);
}
// 상담 불가능
else {
dp[i] = dp[i+1];
}
}
Max = dp[1];
// [output]
System.out.println(Max);
}// main
}
dp[i]가 무엇을 의미하는가?
dp[i] = i번째 ~ 퇴사일까지 벌 수 있는 최대 수익Bottom-up DP에서 뒤에서부터 채우는 이유?
dp[i]를 계산하려면 i 이후의 날(dp[i+1], dp[i+Ti[i]])의 값이 이미 계산되어 있어야 함[시간복잡도]
⇒ 작은 N(예: ≤ 15)에서는 가능, N이 커지면 폭발적 증가
[시간복잡도]
⇒ N이 크더라도 충분히 빠름
이 코드에서 잘못된 부분이나 개선할 점이 있다면 언제든지 댓글로 알려주세요.
알고리즘을 작성하면서 더 좋은 코드를 만들 수 있도록 도와주시면 정말 감사하겠습니다! 🙂