1. 문제

12865번: 평범한 배낭


2. 개요

냅색 알고리즘을 활용하는 dp문제.

가방의 최대무게와 물건을 각각 행,열로 하는 표를 만들어보자.

  6 / 13 4 / 8 3 / 6 5 / 12
1        
2        
3        
4        
5        
6        
7        

각 물건은 가방의 최대무게가 물건의 무게 이상인 경우에만 들어갈 수 있다.

첫 물건부터 넣어보자.

  6 / 13 4 / 8 3 / 6 5 / 12
1 0      
2 0      
3 0      
4 0      
5 0      
6 13      
7 13      

이어서 두 번째 물건을 넣어보자.

  6 / 13 4 / 8 3 / 6 5 / 12
1 0 0    
2 0 0    
3 0 0    
4 0 8    
5 0 8    
6 13 13    
7 13 13    

최대 무게가 6, 7 인 경우, 두번째 물건을 그냥 넣게 되면 최대 무게를 초과하게 된다.

따라서 이런 경우, 이미 들어있는 물건을 빼고 현재 물건을 넣거나, 넣지 않거나 선택을 해야 한다.

전 물건을 넣던 상황에서 현재 물건의 무게를 뺀 최대무게의 가치 + 현 물건의 가치와 전 물건의 지금 고려하는 최대무게의 상황을 비교해야 한다.

최대 무게 6의 경우

→ 첫 물건 최대무게 2의 상황 (0) + 현 물건의 가치 (8)와 최대 무게 6인 경우 첫 물건의 상황(13)

최대 무게 7의 경우

→ 첫 물건 최대무게 3의 상황 (0) + 현 물건의 가치 (8)와 최대 무게 7인 경우 첫 물건의 상황(13)

둘 다 후자가 더 가치가 크므로 칸에는 13이 들어간다.

  6 / 13 4 / 8 3 / 6 5 / 12
1 0 0 0  
2 0 0 0  
3 0 0 6  
4 0 8 8  
5 0 8 8  
6 13 13 13  
7 13 13 14  

세 번째 물건의 최대 무게 7의 경우도 위와 같이 해결한다.

첫 물건 최대무게 4의 상황 (8) + 현 물건의 가치 (6)와 최대 무게 7인 경우 두번째 물건의 상황(13)

14 > 13 이므로 칸에는 14가 들어가게 된다.

같은 방식으로 마지막까지 완성하면

  6 / 13 4 / 8 3 / 6 5 / 12  
1 0 0 0 0  
2 0 0 0 0  
3 0 0 6 6  
4 0 8 8 8  
5 0 8 8 12  
6 13 13 13 13  
7 13 13 14 14  




3. 코드 및 추가내용

3-1. 시도 1

import sys

N, K = map(int,sys.stdin.readline().split())

items = []
for i in range(N):
    W, V = map(int,sys.stdin.readline().split())
    items.append([W, V])

dp = [[0 for i in range(K+1)] for i in range(N+1)]

for i in range(1, N+1):
    for j in range(items[i-1][0], K+1):
        dp[i][j] = max(dp[i-1][j], items[i-1][1] + dp[i-1][j - items[i-1][0]])

print(max(dp[-1]))

첫 시도 실패.

문제가 뭔지 찾아보려고 만들어진 dp테이블을 살펴본 결과

j의 범위를 지정할 때 시작위치를 현재 물건의 무게부터 시작했더니, 현재 물건보다 가벼운 무게의 물건으로 만든 경우를 가져오지 못하는 상황이 발생하여 실패.

3-2. 시도 2

import sys

N, K = map(int,sys.stdin.readline().split())

items = []
for i in range(N):
    W, V = map(int,sys.stdin.readline().split())
    items.append([W, V])

dp = [[0 for i in range(K+1)] for i in range(N+1)]

for i in range(1, N+1):
    for j in range(1, K+1):
        if j >= items[i-1][0]:
            dp[i][j] = max(dp[i-1][j], items[i-1][1] + dp[i-1][j - items[i-1][0]])
        else:
            dp[i][j] = dp[i-1][j]

print(max(dp[-1]))

그래서 j를 전체범위(0은 안넣는 경우이기 때문에 제외) 로 설정하고 현재 물건의 무게보다 가벼운 경우라면 dp테이블의 전 행의 같은 열의 결과를 그대로 가져오는 방법으로 통과했다.

Comments