백준 #12865 평범한 배낭 (파이썬) : 0-1 배낭 문제

Yongjun Park·2022년 5월 8일
0

오늘의 한 마디
2차원 dp배열을 사용하는 문제들에 익숙해지자!
이 알고리즘도 외우자!

문제

이 문제는 아주 평범한 배낭에 관한 문제이다.

한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.

준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.

입력

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다.

입력으로 주어지는 모든 수는 정수이다.

출력

한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.

예제 입력 1

4 7
6 13
4 8
3 6
5 12

예제 출력 1

14

발상

나무위키, 배낭 문제

위 링크에서 소개된 것처럼, 분할 가능한 배낭 문제는 진짜 너무 쉽다.

무게 대비 가치가 가장 높은 것부터 최대한 욱여넣다가,
안 들어가면 잘라서 넣을 수 있는 최선을 다해 넣으면 끝 아닌가?

그냥 이건 그리디 알고리즘이다.

하지만, 이번 문제의 경우에는 물건을 분할할 수 없다.
못 넣는 걸 잘라서 넣을 수 없다!

넣거나, 안 넣거나 두 가지 경우밖에 없다고 해서 0-1(True-False) 배낭 문제라는 이름이 붙었다.

0-1 배낭 문제(Knapsack Problem)

2차원 dp 배열을 사용하는게 기본 발상이다.

y축에는 물건 N개 만큼의 배열, x축에는 가방 1~K까지의 무게 배열을 만든다.

dp = [[0]*(K+1) for _ in range(N+1)]
for y in range(1, N+1):
    w, v = bag[y]
    for x in range(1, K+1):
        if x-w <= 0:
            dp[y][x] = dp[y-1][x] # 이래서 N+1개 만들어야 되는구나
        else:
            dp[y][x] = max(dp[y-1][x], dp[y-1][x-w] + v)

우선 간단히 개념을 살펴보자.
예시로 dp[2][5]에 들어가 있는 값은, 2번 물건까지만 고려했을 때 가방 무게가 5라면 담을 수 있는 최대 가치다.

따라서 배열이 아래로, 오른쪽으로 갈 때 값이 줄어드는 경우는 발생하지 않는다. 점점 늘어날 뿐이다.

print(dp[N][K])의 값이 N번 물건까지 고려했을 때 가방 물건이 K라면 담을 수 있는 최대 가치가 되며, 이것이 바로 문제에서 원하는 답이다.

이제 더 자세히 살펴보자.

  1. dp[y][x] = dp[y-1][x]의 의미는, y번째 물건은 넣지 않겠다는 뜻이다.
  2. dp[y][x] = dp[y-1][x-w] + v의 의미는, y번째 물건을 넣겠다는 의미다. 이 때, dp[y-1][x]가 아닌 dp[y-1][x-w]에서 가져오는 것이 포인트다.
  3. dp[y][x] = max(dp[y-1][x], dp[y-1][x-w] + v)의 의미는, y번째 물건을 넣지 않고 x의 무게를 만드는 경우와 y번째 물건을 넣고 x의 무게를 만드는 경우 중에 더 가치가 높은 것을 선택하겠다는 의미다!

풀이

import sys

N, K = map(int, sys.stdin.readline().rstrip().split())
bag = [(0, 0)]
for _ in range(N):
    W, V = map(int, sys.stdin.readline().rstrip().split())
    bag.append((W, V))
    
dp = [[0]*(K+1) for _ in range(N+1)]

for y in range(1, N+1):
    w, v = bag[y]
    for x in range(1, K+1):
        if x-w <= 0:
            dp[y][x] = dp[y-1][x] # 이래서 N+1개 만들어야 되는구나
        else:
            dp[y][x] = max(dp[y-1][x], dp[y-1][x-w] + v)

print(dp[N][K])
profile
추상화되었던 기술을 밑단까지 이해했을 때의 쾌감을 잊지 못합니다

0개의 댓글