문제
이 문제는 아주 평범한 배낭에 관한 문제이다.
한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.
준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.
더 쉬운 비슷한 유형의 DP 문제
문제 풀이
개인적으로 다이나믹 프로그램은 난이도가 높다고 생각한다. 그 이유는 DP 테이블 설계하는 게 생각보다 쉽지 않다. 시간복잡도에 따른 적절한 접근 방법을 고르는 게 익숙하지 않기 때문인 것 같다. 이전에는 항상 브루트포스 관점으로만 접근했었기 때문이다. 여러 관점에서 문제를 살펴봐보자.
1. 브루트포스
1≤N≤100인데 브루트포스로 모든 경우의 수를 살펴볼 수 있나?
•
1억번의 연산이 훨씬 넘어버린다. 불가능하다
2. 그리디
가치가 가장 큰 것을 선택하는게 최선의 선택이 아니다. 따라서 그리디가 적합하지 않다.
3. DP
대표적인 다이나믹프로그래밍 문제이다. 이전 문자열의 LCS(9251) 문제 풀이에서 살펴본 것처럼 가장 어려웠던 부분은 DP에 어떤 값을 저장할지 생각하고 설계하는 부분이다. 관계식을 찾는 부분이 가장 어려웠다.
만약, 다이나믹 프로그래밍을 사용이 가능하다면 시간 복잡도는 다음과 같다.
•
DP 배열에 저장할 정보
•
DP[N][K] : n번 물건까지 넣을 수 있고, 무게 합이 k 이하일 때의 최대 가치.
DP 관계식
생각하기 가장 어려운 부분. 이전의 최대가치를 어떻게 가져올 것인가를 생각해야 한다. 이전에 풀었던 문제지만, DP 테이블을 설계하는 게 여전히 쉽지 않다.
•
dp[n][k]을 갱신하기 위해선 을 접근해야 한다.
◦
: n번 물건의 무게, : n번 무게의 가치
•
DP 테이블을 전체를 작성해야 되는 경우가 아니기 때문에, Top-Down 방식이 효율적이다.
// 테이블의 범위를 벗어나면 안된다.
if (k - w >= 0) {
dp[n][k] = Math.max(dp[n-1][k], dp[n-1][k - w] + v)
}
JavaScript
복사
Javascript 코드
function solution(input) {
const [N, K] = input[0].trim().split(' ').map(Number);
const arr = input.slice(1).map((el) => el.trim().split(' ').map(Number));
const W = [0, ...arr.map((el) => el[0])];
const V = [0, ...arr.map((el) => el[1])];
let dp = Array(N + 1)
.fill()
.map(() => Array(K + 1).fill(0)); //i kg을 담았을 때 최대가치, dy[m]이 답이 된다.
for (let n = 1; n <= N; n += 1) {
for (let k = 1; k <= K; k += 1) {
dp[n][k] = dp[n - 1][k];
if (k - W[n] >= 0) {
dp[n][k] = Math.max(dp[n][k], dp[n - 1][k - W[n]] + V[n]);
}
}
}
return dp[N][K];
}
console.log(solution(input));
Python
복사
Python 코드
N, K = map(int, input().split())
W = [0]
V = [0]
for _ in range(N):
w, v = map(int, input().split())
W.append(w)
V.append(v)
dp = [[0 for _ in range(K + 1)] for _ in range(N + 1)]
for n in range(1, N + 1):
for k in range(1, K + 1): # dp[n][k]
dp[n][k] = dp[n - 1][k]
if k - W[n] >= 0:
dp[n][k] = max(dp[n][k], dp[n - 1][k - W[n]] + V[n])
print(dp[N][K])
Python
복사