
시험까지 남은 총 공부 시간 T가 주어지고,
각 단원마다 공부에 필요한 시간(K) 과 얻을 수 있는 점수(S) 가 주어진다.
공부할 수 있는 시간의 총합이 T를 넘지 않도록 단원들을 선택했을 때,
얻을 수 있는 최대 점수를 구하는 문제다.
즉, 이 문제는 전형적인 0-1 배낭 문제(Knapsack) 형태이며,
각 단원은 한 번만 선택할 수 있다.
이 문제는 "단원"을 "물건"으로, "공부 시간"을 "무게"로 치환한 형태의 Knapsack 응용이다.
time = 공부 소요 시간, score = 단원 점수 T (총 공부 가능 시간) 각 시간마다 “그 시점까지의 최대 점수”를 저장하며,
매 단원마다 뒤에서부터 dp 갱신(0-1 Knapsack 방식)을 반복한다.
평범한 배낭 문제와 완벽하게 일치하는 문제
dp[i]: i시간 동안 공부했을 때 얻을 수 있는 최대 점수dp[0] = 0 (시간이 0이면 아무 공부도 못 하므로 점수도 0)각 단원에 대해,
현재 공부 시간 time, 점수 score를 가지고,
현재 제한 시간부터 거꾸로 갱신한다.
dp[j] = max(dp[j], dp[j - time] + score)
단, j >= time일 때만 가능.
이렇게 뒤에서부터 갱신해야
한 단원을 여러 번 선택하는 것을 방지할 수 있다.
dp[t] = 최대 공부시간 t 안에서 얻을 수 있는 최대 점수package A5DP.Baekjoon;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class G14728벼락치기 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // 단원 개수
int t = Integer.parseInt(st.nextToken()); // 총 공부 가능 시간
int[][] timeWithScore = new int[n][2];
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
timeWithScore[i][0] = Integer.parseInt(st.nextToken()); // 공부 시간
timeWithScore[i][1] = Integer.parseInt(st.nextToken()); // 점수
}
int[] dp = new int[t + 1];
for (int i = 0; i < n; i++) {
int time = timeWithScore[i][0];
int score = timeWithScore[i][1];
for (int j = t; j >= time; j--) {
dp[j] = Math.max(dp[j], dp[j - time] + score);
}
}
System.out.println(dp[t]);
}
}
예를 들어,
T = 7, 단원이 다음과 같이 주어진 경우를 보자.
| 단원 | 필요 시간(K) | 점수(S) |
|---|---|---|
| 1 | 2 | 4 |
| 2 | 4 | 7 |
| 3 | 3 | 5 |
for j = t → time)으로 순회. 1차원 Rolling DP). dp[t]가 곧 답.