Knapsack 유형
- 0-1 Knapsack
- 쪼갤 수 없는 물건을 배낭에 담는 문제
- Dynamic programming 문제
- Fractional Knapsack
- 쪼갤 수 있는 물건을 배낭에 담는 문제
- Greedy 문제
Q. 10kg의 용량의 배낭에 다음과 같은 4가지의 물건을 넣을 수 있을 때, 최대의 가치가 되도록 배낭을 채우려면?
- 5kg/10만원, 4kg/60만원, 3kg/10만원, 6kg/40만원
💡 생각할 포인트! - 상향식 DP, 가방안에 부분 가방!!
물건 i에 대해 가방의 무게를 w로 했을 경우
DP 배열 정의
dp[i][w] : 물건1번부터 i까지를 고려하여 가방 무게 w이하를 만족 시킬 때의 최대 가치
- dp[i]가 필요한 이유 : 각 물건이 하나만 있기 때문에 현재 물건 i를 선택하면 물건 i를 선택하지 않은 i-1까지에서만 최대 value를 구해야한다.
만약 물건을 중복하여 선택해도 된다면, 현재 물건을 선택함에 따라 이전 물건까지만 고려하는 것을 안해도 된다. -> Unbounded Knapsack Problem
점화식
K : dp배열, V: 가치, W : 부분 배낭의 무게, i:현재 고려해야 할 물건
- 물건 i가 가방 무게 w이하인 경우 (담을 수 있다)
k[i][w] = MAX(Vi + k[i-1][W-wi], k[i-1][w])- 물건 i가 가방 무게 w를 초과인 경우 (담지 못한다) - 직전 값 저장
k[i-1][w]
/*
* dp[i][w] = 배낭 w일때, 물건 i까지의 최대 가치
*
* dp[i][w] =
* 물건 i를 담지 않는다 -> 이전(i-1) 물건을 담는 것 까지의 최대 가치 = dp[i-1][w];
* 물건 i를 담는다 -> 현재 물건(i)의 가치 + 가방 무게(w)에서 현재 물건(i)의 무게를 뺀 최대 가치 중 더 큰 가치 = Max(dp[i-1][w], value[i] + dp[i-1][w - iw])
*
* */
int n = Integer.parseInt(st.nextToken()); // 물품 수
int W = Integer.parseInt(st.nextToken()); // 가방 무게
int[][] goods = new int[n+1][2];
for(int i=1;i<=n;i++){
st = new StringTokenizer(br.readLine());
int iw = Integer.parseInt(st.nextToken()); // 물건 무게
int iv = Integer.parseInt(st.nextToken()); // 물건 가치
goods[i][0] = iw;
goods[i][1] = iv;
}
int[][] dp = new int[n+1][W+1];
for(int i=1;i<=n;i++){ // 물건
for(int j=1;j<=W;j++) { // 임시 가방
int iw = goods[i][0]; // 물건 무게
int iv = goods[i][1]; // 물건 가치
if (iw > j) { // 물건 무게가 임시 가방 보다 클 때,
dp[i][j] = dp[i-1][j]; // 이전 물건까지의 최대 가치
}else{
dp[i][j] = Math.max(dp[i-1][j], iv + dp[i-1][j-iw]);
}
}
}
System.out.println(dp[n][W]);
방법1. 2차원 배열의 2행까지만 사용하기
방법2. 역순으로 고려하여 일차원 배열 하나만 사용하기
int[] dp = new int[W+1];
for(int i=1;i<=n;i++){ // 물건
for(int j=W;j>=1;j--) { // 임시 가방 (역순으로 하여 물건을 중복으로 선택하는 것을 제거)
int iw = goods[i][0]; // 물건 무게
int iv = goods[i][1]; // 물건 가치
if (iw <= j) {
dp[j] = Math.max(dp[j], iv + dp[j-iw]);
}
}
}
System.out.println(dp[W]);